WPF MVVM有IValueConverter的替代方案

本文关键字:方案 IValueConverter MVVM WPF | 更新日期: 2023-09-27 18:10:18

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

  1. 状态将决定文本旁边显示的形状以及该形状的描边和填充颜色。
  2. 状态将决定该项目文本的背景颜色和文本颜色。
  3. 状态将决定是否在项目中显示倒计时计时器(计时器与视图模型没有关联)
  4. 状态可以决定父容器的边框颜色。

我目前在每个属性的单独IValueConverters中执行此逻辑。它工作,但感觉笨重和分散。我几乎想以某种方式订阅UI中的PropertyChanged事件,并让它调用一个方法来呈现该项目的所有显示,以便所有逻辑都包含在一个地方。是否有更好的方法来做到这一点,或者我应该坚持IValueConverters?

这是我所拥有的一个例子。

public ObservableCollection<PanelItem> PanelItems1 
{
    get { return panelItems1; }
    set
    {
        panelItems1 = value;
        base.OnPropertyChanged("PanelItems1");
    }
}

PanelItem是一个小的属性集合,包括:名称,值(状态),描述。ItemsControls类似于以下内容:

<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>

WPF MVVM有IValueConverter的替代方案

为什么不声明一个普通的类来实现Status对象的INotifyPropertyChanged接口呢?只需添加所需的属性,如Geometry, FillStroke等。如果你这样做了,你就不需要任何Converter类,你可以这样做:

<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 = ...
            };
    }
}

那么你所需要做的就是修改你的PathDataContext属性来使用这个转换器。

<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>