在资源中创建一个控件并在XAML WPF中重复使用

35

我尝试创建一个简单的符号/几何体/控件,并在同一个窗口中的多个位置更改和重用它。

例如:一个黑色的正方形,中间有一个圆。

圆应该在红色和绿色之间切换(类似于单灯红绿灯)。使用图像也可以实现。我尝试将其作为窗口资源解决,但我不理解。

我的想法是将它写入资源中,在这里我尝试将它写入画布中:

<Window.Resources>
    <Canvas x:Key="Ampel">
        <Rectangle Fill="Black" HorizontalAlignment="Left" Height="52" Stroke="Black"       VerticalAlignment="Top" Width="50"/>
        <Ellipse x:Name="RedGreen" Fill="Red" HorizontalAlignment="Left" Height="27" Margin="11,12,0,0" Stroke="Black" VerticalAlignment="Top" Width="28" RenderTransformOrigin="0.214,0.256"/>
    </Canvas>
</Window.Resources>

那么我想将它放在一个 Grid 或者 Panel 中,但是如何引用它呢?

<Canvas x:Name="RedGreen1" Height="50" Width="50" DataContext="{DynamicResource Ampel}" /> 

这段代码并不会返回编译错误,但窗口中也没有显示任何内容。它也无法与WrapPanel或其他任何控件一起使用。

如果它能够工作,我该如何在代码中引用它以改变圆形的颜色呢?类似于RedGreen1.RedGreen.Fill=Brushes.Green这样的语句?

我读了有关交通信号灯的文章。是否真的需要创建一个UserControl,还是可以通过window.resources解决问题?

应用程序的基本想法是有一个参数列表,每个参数都有一个正确的输入值,标记为绿色,并且只有所有参数都标记为绿色时才能开始计算。

即使我使用红/绿图像使其运行,我仍然希望更好地理解WPF / XAML并学到更多。

谢谢。

3个回答

36

当您在资源中定义一个任意的控件时,可以在具有属性Content且派生自Control类的控件中将其用于未来。这些控件包括: ContentControlLabelContentPresenter等。

如果您想在多个控件中使用此资源,则还必须为资源设置x:Shared="False",因为默认情况下x:Shared="True",则一个资源对所有控件都是共享的 - 在这种情况下,系统会出现重复内容的问题。而x:Shared="False"则会为每个元素创建一个资源,每次请求都会创建一个新实例。引用自MSDN

当设置为 false 时,修改 WPF 资源检索行为,以便对所属性的资源的请求为每个请求创建一个新实例,而不是共享所有请求的同一实例。

示例:

<Window.Resources>
    <Canvas x:Key="Ampel" x:Shared="False">
        <Rectangle Fill="Black" HorizontalAlignment="Left" Height="52" Stroke="Black" VerticalAlignment="Top" Width="50"/>
        <Ellipse x:Name="RedGreen" Fill="Red" HorizontalAlignment="Left" Height="27" Margin="11,12,0,0" Stroke="Black" VerticalAlignment="Top" Width="28" />
    </Canvas>
</Window.Resources>

<Grid>
    <ContentControl Name="MyContentControl" Content="{StaticResource Ampel}" HorizontalAlignment="Left" />        
    <Label Name="MyLabel" Content="{StaticResource Ampel}" HorizontalAlignment="Center" />
    <ContentPresenter Name="MyContentPresenter" Content="{StaticResource Ampel}" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
在代码后端更改椭圆的填充,你可以像这样操作:

Fill

private void ChangeBackground_Click(object sender, RoutedEventArgs e)
{
    var canvas = MyContentControl.Content as Canvas;

    if (canvas != null)
    {
        foreach (var item in canvas.Children)
        {
            if (item is Ellipse)
            {
                ((Ellipse)item).Fill = Brushes.Green;
            }
        }
    }
}

4
x:Shared="False" 起了作用。有趣的是它在自动完成中不会显示。 - JobaDiniz
1
你为什么要更改代码后端的填充?这与 MVVM 不同步。 - user853710
1
你将其绑定到一个属性,绑定到一个转换器。 - user853710

8

Canvas没有模板属性,因此我们在这里使用ContentControl。

<Window.Resources>
    <ControlTemplate x:Key="Ampel" TargetType="ContentControl">
        <Canvas>
            <Rectangle Fill="Black" HorizontalAlignment="Left" Height="52" Stroke="Black" VerticalAlignment="Top" Width="50"/>
            <Ellipse x:Name="RedGreen" Fill="{Binding Path=Tag, RelativeSource={RelativeSource TemplatedParent}}" HorizontalAlignment="Left" Height="27" Margin="11,12,0,0" Stroke="Black" VerticalAlignment="Top" Width="28" RenderTransformOrigin="0.214,0.256"/>
        </Canvas>
    </ControlTemplate>
</Window.Resources >

<ContentControl  Template="{StaticResource Ampel}" Tag="Red" ></ContentControl>
<ContentControl  Template="{StaticResource Ampel}" Tag="Green" ></ContentControl>
<ContentControl  Template="{StaticResource Ampel}" Tag="Blue" ></ContentControl>

输出

enter image description here


请参见Anatoliy Nikolaev的答案,对我来说,使用共享想法似乎更加简洁。不过还是非常感谢。 - user3367867

-2

你可以通过继承来使用C#代码:

public class customBottum : Buttom
{
        int var1;
        bool var2;

        public customBottum()
        {
            InitializeComponent();
        }

        other function ...
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接