C# WPF MVVM是否有替代IValueConverter的方法

C# WPF MVVM是否有替代IValueConverter的方法,c#,wpf,mvvm,C#,Wpf,Mvvm,我有几组可观察到的项目集合,它们在后台线程上定期更新(30到60秒)。这些集合通过视图上的ItemsControl显示。父项和控件中的项具有多个与每个项的状态关联的显示属性 状态将决定文本旁边显示的形状以及该形状的笔划和填充颜色 状态将决定该项目文本的背景色和文本颜色 状态将确定项目中是否显示倒计时计时器(计时器与viewmodel没有连接) 状态可能决定父容器的边框颜色 我目前正在为每个属性的单个iValueConverter中执行此逻辑。它可以工作,但感觉很累赘,而且很分散。我几乎想以某种方

我有几组可观察到的项目集合,它们在后台线程上定期更新(30到60秒)。这些集合通过视图上的ItemsControl显示。父项和控件中的项具有多个与每个项的状态关联的显示属性

  • 状态将决定文本旁边显示的形状以及该形状的笔划和填充颜色
  • 状态将决定该项目文本的背景色和文本颜色
  • 状态将确定项目中是否显示倒计时计时器(计时器与viewmodel没有连接)
  • 状态可能决定父容器的边框颜色
  • 我目前正在为每个属性的单个iValueConverter中执行此逻辑。它可以工作,但感觉很累赘,而且很分散。我几乎想以某种方式订阅UI中的PropertyChanged事件,并让它调用一个方法来呈现该项的所有显示,以便所有逻辑都包含在一个地方。有没有更好的方法来实现这一点,还是我应该坚持下去

    这是我的一个例子

    收藏:

    public ObservableCollection<PanelItem> PanelItems1 
    {
        get { return panelItems1; }
        set
        {
            panelItems1 = value;
            base.OnPropertyChanged("PanelItems1");
        }
    }
    
    公共可观察收集面板项目1
    {
    获取{return panelItems1;}
    设置
    {
    panelItems1=值;
    基于财产变更(“面板项目1”);
    }
    }
    
    PanelItem是一个小的属性集合,包括:名称、值(状态)、描述。ItemsControl类似于以下内容:

    <Border Grid.Row="0" Grid.Column="0"
            BorderBrush="{Binding Path=PanelGroup1.HighestStatus, Converter={StaticResource ParentBorderColorConverter}}"
            BorderThickness="3" Height="Auto" Width="Auto" Margin="0,0,2,0">
        <StackPanel Grid.Column="0" Grid.Row="0">
            <Label Grid.Row="0" Grid.Column="0" Content="First Group" Style="{StaticResource panelTitle}" />
            <ItemsControl ItemsSource="{Binding Path=PanelItems1, Mode=TwoWay}"
                          ItemTemplate="{StaticResource PanelItemTemplate}">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <StackPanel IsItemsHost="True" />
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
            </ItemsControl>
    
            <!-- Here is the Data Template -->
            <DataTemplate x:Key="PanelItemTemplate">
                <Viewbox MaxHeight="20" HorizontalAlignment="Left" Cursor="Hand">
                    <WrapPanel>
                        <Path Margin="2,2,2,2" StrokeThickness="2">
                            <Path.Data>
                                <Binding Path="Status" Mode="OneWay" Converter="{StaticResource ShapeConverter}" />
                            </Path.Data>
                            <Path.Fill>
                                <Binding Path="Status" Mode="OneWay" Converter="{StaticResource ShapeColorConverter}" />
                            </Path.Fill>
                            <Path.Stroke>
                                <Binding Path="Status" Mode="OneWay" Converter="{StaticResource ShapeBorderConverter}" />
                            </Path.Stroke>
                        </Path>
                        <ContentPresenter Content="{Binding Path=Name}" Margin="5,0,0,0" />
                    </WrapPanel>
                </Viewbox>
            </DataTemplate>
        </StackPanel>
    </Border>
    

    为什么不声明一个普通类来实现
    状态对象的
    INotifyPropertyChanged
    接口?只需在其中添加所需的属性,例如
    几何体
    填充
    笔划
    等。。。如果您这样做了,就不需要任何
    转换器
    类,您可以这样做:

    <DataTemplate x:Key="PanelItemTemplate">
        <Viewbox MaxHeight="20" HorizontalAlignment="Left" Cursor="Hand" > 
               <WrapPanel>
                    <Path Margin="2,2,2,2" StrokeThickness="2" >
                    <Path.Data>
                        <Binding Path="Status.Geometry" Mode="OneWay" />
                    </Path.Data>
                    <Path.Fill>
                        <Binding Path="Status.Fill" Mode="OneWay" />
                    </Path.Fill>
                    <Path.Stroke>
                        <Binding Path="Status.Stroke" Mode="OneWay" />
                    </Path.Stroke>
                </Path>
                <ContentPresenter  Content="{Binding Path=Name}" Margin="5,0,0,0"   />
            </WrapPanel>
        </Viewbox>
    </DataTemplate>
    

    可以只创建一个转换器,其中包含所有转换逻辑,并返回一个新类,您将绑定到该类:

    public class MyNewConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            // Perform all required conversions here and return them as a new type of object
            return new
                {
                    Data = ...,
                    Fill = ...,
                    Stroke = ...
                };
        }
    }
    
    然后,您只需修改
    路径
    数据上下文
    属性即可使用此转换器

    <Viewbox MaxHeight="20" HorizontalAlignment="Left" Cursor="Hand">
        <WrapPanel>
            <Path Margin="2,2,2,2" StrokeThickness="2" DataContext="{Binding Path=Status, Converter={StaticResource MyNewConverter}}">
                <Path.Data>
                    <Binding Path="Data" Mode="OneWay" />
                </Path.Data>
                <Path.Fill>
                    <Binding Path="Fill" Mode="OneWay" />
                </Path.Fill>
                <Path.Stroke>
                    <Binding Path="Stroke" Mode="OneWay" />
                </Path.Stroke>
            </Path>
            <ContentPresenter Content="{Binding Path=Name}" Margin="5,0,0,0" />
        </WrapPanel>
    </Viewbox>
    
    
    
    您是否考虑过使用可视状态管理器?您可以使用VSM滚动您自己的控件模板以备份任何可视状态-显然,它需要知道它绑定到什么,以便有条件地应用这些状态-您也可以使用触发器而不是值转换器,而不是公开单个状态并应用3个不同的转换器,你可以在你的虚拟机上公开3个不同的属性,并进行3次转换,以在虚拟机中(或在虚拟机调用的逻辑中)给它们一个值。检查这里,了解有关VSM的信息-还有关于触发器的相关信息-我总是将值转换器视为转换值的一种方式,不触发视觉状态,所以我尝试在任何麻烦的事情上避免它们,因为它们最终是代码而不是markup@Charleh我以前没见过这个,很有趣。我现在正在读。它看起来更多地用于UI交互事件,但我可能能够将其绑定到viewmodel上的属性。看起来我必须为每个控件添加一个VSM,然后根据视图切换视觉状态。@stijn当您说在VM上公开3个属性时,这些属性是颜色吗?如果是这样,它会将视图特定的显示知识放入ViewModel。请记住,像
    Fill
    Stroke
    这样的属性表示视觉状态,如果您打算正确遵循MVVM模式,则不应成为ViewModel的一部分。“应该”并不意味着“必须”我喜欢这个想法,我可以创建一个包含该逻辑的类。我的问题是它适合哪里?视图的DataContext设置为my ViewModel,items控件绑定到该ViewModel上的一组项,如何在它们之间插入新的状态类?我同意Adi,
    Fill
    Stroke
    不应该是VM的一部分,因为视图应该根据VM内容决定视觉状态。你可以打破这条规则,但是如果你把视图交给用户体验/用户界面设计师,而他们为了改变视觉状态而不得不与视图模型混为一谈,那就有点违背了MVVM的全部目的。使用触发器或VSM可以很容易地获取可视状态以对viewmodel属性作出反应。你可以对MVVM“半严格”,但如果你能找到一种不“作弊”的方法,那么你就是在学习,通常是在创建一个更健壮的解决方案。然而,反过来说,如果你的VM是特定于应用程序的,并且包装了一个特定的数据项,那么你可能会认为它与视图密切相关。如果您正在重用viewmodels,那么很可能是错误的方法我非常喜欢这个想法!我刚才遇到的唯一障碍是一些额外的要求,需要在某些状态下闪烁背景色(可能是故事板?)和声音通知。我不知道该怎么开始