如何参数化WPF样式?

发布于 2024-08-25 09:30:38 字数 2656 浏览 6 评论 0原文

我正在寻找一种最简单的方法来删除 WPF 代码中的重复项。

下面的代码是一个简单的交通灯,有 3 个灯 - 红色琥珀色绿色。它绑定到一个 ViewModel,该 ViewModel 有一个枚举属性 State,采用这 3 个值之一。

声明 3 个省略号的代码非常重复。现在我想添加动画,以便每个灯光淡入和淡出 - 样式会变得更大,重复会变得更糟。

是否可以使用 StateColor 参数对样式进行参数化,以便我可以在描述灯光行为的资源中使用单一样式,然后使用它 3 次 - 对于“红色” ”、“琥珀色”和“绿色”灯?

<UserControl.Resources>
    <l:TrafficLightViewModel x:Key="ViewModel" />
</UserControl.Resources>

<StackPanel Orientation="Vertical" DataContext="{StaticResource ViewModel}">
    <StackPanel.Resources>
        <Style x:Key="singleLightStyle" TargetType="{x:Type Ellipse}">
            <Setter Property="StrokeThickness" Value="2" />
            <Setter Property="Stroke" Value="Black" />
            <Setter Property="Height" Value="{Binding Width, RelativeSource={RelativeSource Self}}" />
            <Setter Property="Width" Value="60" />
            <Setter Property="Fill" Value="LightGray" />
        </Style>
    </StackPanel.Resources>

    <Ellipse>
        <Ellipse.Style>
            <Style TargetType="{x:Type Ellipse}" BasedOn="{StaticResource singleLightStyle}">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding State}" Value="Red">
                        <Setter Property="Fill" Value="Red" />
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </Ellipse.Style>
    </Ellipse>
    <Ellipse>
        <Ellipse.Style>
            <Style TargetType="{x:Type Ellipse}" BasedOn="{StaticResource singleLightStyle}">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding State}" Value="Amber">
                        <Setter Property="Fill" Value="Red" />
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </Ellipse.Style>
    </Ellipse>
    <Ellipse>
        <Ellipse.Style>
            <Style TargetType="{x:Type Ellipse}" BasedOn="{StaticResource singleLightStyle}">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding State}" Value="Green">
                        <Setter Property="Fill" Value="Green" />
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </Ellipse.Style>
    </Ellipse>
</StackPanel>

I'm looking for a simplest way to remove duplication in my WPF code.

Code below is a simple traffic light with 3 lights - Red, Amber, Green. It is bound to a ViewModel that has one enum property State taking one of those 3 values.

Code declaring 3 ellipses is very duplicative. Now I want to add animation so that each light fades in and out - styles will become even bigger and duplication will worsen.

Is it possible to parametrize style with State and Color arguments so that I can have a single style in resources describing behavior of a light and then use it 3 times - for 'Red', 'Amber' and 'Green' lights?

<UserControl.Resources>
    <l:TrafficLightViewModel x:Key="ViewModel" />
</UserControl.Resources>

<StackPanel Orientation="Vertical" DataContext="{StaticResource ViewModel}">
    <StackPanel.Resources>
        <Style x:Key="singleLightStyle" TargetType="{x:Type Ellipse}">
            <Setter Property="StrokeThickness" Value="2" />
            <Setter Property="Stroke" Value="Black" />
            <Setter Property="Height" Value="{Binding Width, RelativeSource={RelativeSource Self}}" />
            <Setter Property="Width" Value="60" />
            <Setter Property="Fill" Value="LightGray" />
        </Style>
    </StackPanel.Resources>

    <Ellipse>
        <Ellipse.Style>
            <Style TargetType="{x:Type Ellipse}" BasedOn="{StaticResource singleLightStyle}">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding State}" Value="Red">
                        <Setter Property="Fill" Value="Red" />
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </Ellipse.Style>
    </Ellipse>
    <Ellipse>
        <Ellipse.Style>
            <Style TargetType="{x:Type Ellipse}" BasedOn="{StaticResource singleLightStyle}">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding State}" Value="Amber">
                        <Setter Property="Fill" Value="Red" />
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </Ellipse.Style>
    </Ellipse>
    <Ellipse>
        <Ellipse.Style>
            <Style TargetType="{x:Type Ellipse}" BasedOn="{StaticResource singleLightStyle}">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding State}" Value="Green">
                        <Setter Property="Fill" Value="Green" />
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </Ellipse.Style>
    </Ellipse>
</StackPanel>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

明月松间行 2024-09-01 09:30:38

只要您的“交通灯”包含在控件内(看起来确实如此),我认为这并不可怕。每个椭圆都有明确的定义并具有不同的触发器,每个触发器都指示其自己的状态。您已经将公共部分分解到基本样式中,这很好。

您可以将各个省略号包装在另一个具有 ActiveState 属性和 ActiveFill 属性的用户控件(不需要支持 ViewModel)内。然后您的 TrafficLight 看起来像这样:

<StackPanel Orientation="Vertical" DataContext="{StaticResource ViewModel}">
    <my:Indicator State="{Binding State}" ActiveState="Red" ActiveFill="Red" />
    <my:Indicator State="{Binding State}" ActiveState="Amber" ActiveFill="Red" />
    <my:Indicator State="{Binding State}" ActiveState="Green" ActiveFill="Green" />
</StackPanel>

这可以让您将所有 Ellipse 样式包装在 Indicator 控件内,并且该控件唯一需要担心的是将 StateActiveState 进行比较> 确定是否应使用 ActiveFill 画笔填充自身。

至于这是否值得付出努力,这取决于您周围有多少个这些,以及您是否在交通灯用户控制范围之外使用它们。记住:你不会需要它。

As long as your "Traffic Light" is wrapped up inside a control, which it appears it is, I don't think this is horrible. Each ellipse is well defined and has different triggers, each indicating its own state. You've already factored the common parts out into the base style, which is good.

You could wrap the individual ellipses inside another user control (which wouldn't need a backing ViewModel) that had an ActiveState property and an ActiveFill property. Then your TrafficLight looks something like:

<StackPanel Orientation="Vertical" DataContext="{StaticResource ViewModel}">
    <my:Indicator State="{Binding State}" ActiveState="Red" ActiveFill="Red" />
    <my:Indicator State="{Binding State}" ActiveState="Amber" ActiveFill="Red" />
    <my:Indicator State="{Binding State}" ActiveState="Green" ActiveFill="Green" />
</StackPanel>

This lets you wrap up all your Ellipse styling inside your Indicator control and the only thing that control needs to worry about is comparing the State to the ActiveState to determine if it should fill itself with the ActiveFill brush.

As to if this is worth the effort or not, that depends on how many of these you have floating around and if you use them outside of your Traffic Light user control. Remember: You Ain't Gonna Need It.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文