根据DependencyProperty

时间:2017-11-16 18:30:51

标签: c# wpf listview mvvm

我有一个可以在两个不同窗口中使用的wpf控件。该控件包含一个ListView,它由同一个类的ObservableCollection提供,无论哪个窗口托管该控件。

在一个窗口中,我想显示一组特定列,而在另一个窗口中显示一组不同的列。

我已经包含了一个我想要完成的事情的简单例子。出于此示例的目的,xml包含在窗口中而不是UserControl中。

这是定义窗口及其两个ListViews的xaml:

<Window x:Class="ListTest.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:ListTest"
    mc:Ignorable="d"
    Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
    <local:MainViewModel />
</Window.DataContext>
<Window.Resources>
    <ControlTemplate x:Key="listOne"  TargetType="{x:Type ListView}">
        <ListView Margin="10,30,10,10" ItemsSource="{Binding MyList}">
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="Name" Width="120" DisplayMemberBinding="{Binding Name}" />
                    <GridViewColumn Header="Food" Width="50" DisplayMemberBinding="{Binding Food}" />
                </GridView>
            </ListView.View>
        </ListView>
    </ControlTemplate>
    <ControlTemplate x:Key="listTwo"  TargetType="{x:Type ListView}">
        <ListView Margin="10,30,10,10" ItemsSource="{Binding MyList}">
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="Name" Width="120" DisplayMemberBinding="{Binding Name}" />
                    <GridViewColumn Header="Number" Width="120" DisplayMemberBinding="{Binding Number}" />
                    <GridViewColumn Header="State" Width="50" DisplayMemberBinding="{Binding State}" />
                </GridView>
            </ListView.View>
        </ListView>
    </ControlTemplate>
</Window.Resources>
<Grid>
    <CheckBox x:Name="checkBox" Content="Complex" HorizontalAlignment="Left" 
              Margin="10,10,10,10" VerticalAlignment="Top"
              IsChecked="{Binding IsComplex}"/>
    <ListView Margin="10" Name="lvUsers" Template="{StaticResource listTwo}" />
</Grid>

这是我琐碎的viewmodel和记录类:

    public class MyRecord
{
    public MyRecord(string firstName, string food, int number, string state)
    {
        Name = firstName;
        Food = food;
        Number = number;
        State = state;
    }

    public string Name { get; set; }
    public string Food { get; set; }
    public int Number { get; set; }
    public string State { get; set; }
}

public class MainViewModel : ViewModelBase
{
    private List<MyRecord> _recordList;

    public MainViewModel()
    {
        _recordList = new List<MyRecord>();
        _recordList = new List<MyRecord>();
        _recordList.Add(new MyRecord("Lee", "pizza", 10, "ID"));
        _recordList.Add(new MyRecord("Gary", "burger", 20, "UT"));

        MyList = new ObservableCollection<MyRecord>(_recordList);
    }

    private ObservableCollection<MyRecord> _myList;
    public ObservableCollection<MyRecord> MyList
    {
        get { return _myList; }
        set
        {
            if (_myList != value)
            {
                _myList = value;
                OnPropertyChanged(() => MyList);
            }
        }
    }

    private bool _isComplex = true;
    public bool IsComplex
    {
        get { return _isComplex; }
        set
        {
            if (_isComplex != value)
            {
                _isComplex = value;
                OnPropertyChanged(() => IsComplex);
            }
        }
    }
}

xaml的倒数第二行有一个硬编码的模板赋值:

        <ListView Margin="10" Name="lvUsers" Template="{StaticResource listTwo}" />

在xaml中来回更改会导致程序显示一个ListView布局或另一个没有错误。

我希望能够在ViewModel中设置一个属性来控制使用哪个布局 - 在这个简单的例子中,我有一个应该控制所选ListView的复选框。

我尝试了触发器,这似乎是最简单的方法,但是没有找到任何让编译器满意的东西。

任何建议都将不胜感激!

更新 Ed Plunkett的回复告诉我,我的问题太难了。我不想替换整个ListView,只是控制其中显示的列。提取他的一些代码会产生我原本想要的行为,而不会进入代码隐藏并替换整个ListView。我的示例中显示的列现在切换到正确的&#34;视图&#34;当我切换复选框时。代码隐藏不受影响,视图模型保持不变。谢谢艾德!我接受了他的答案,因为它向我展示了我需要的代码子集,并且我已经改变了标题以反映真正的问题。

这是完整修订的xaml:

<Window x:Class="AAWorkTest.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:ListTest"
    mc:Ignorable="d"
    Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
    <local:MainViewModel />
</Window.DataContext>
<Grid>
    <CheckBox x:Name="checkBox" Content="Complex" HorizontalAlignment="Left" 
              Margin="10,10,10,10" VerticalAlignment="Top"
              IsChecked="{Binding IsComplex}"/>
    <ListView ItemsSource="{Binding MyList}" Margin="10,30,10,30" Name="lvUsers">
        <ListView.Style>
            <Style TargetType="ListView">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding IsComplex}" Value="False">
                        <Setter Property="View">
                            <Setter.Value>
                                <GridView>
                                    <GridViewColumn Header="Name" Width="120" DisplayMemberBinding="{Binding Name}" />
                                    <GridViewColumn Header="Food" Width="50" DisplayMemberBinding="{Binding Food}" />
                                </GridView>
                            </Setter.Value>
                        </Setter>
                    </DataTrigger>
                    <DataTrigger Binding="{Binding IsComplex}" Value="True">
                        <Setter Property="View">
                            <Setter.Value>
                                <GridView>
                                    <GridViewColumn Header="Name" Width="120" DisplayMemberBinding="{Binding Name}" />
                                    <GridViewColumn Header="Number" Width="120" DisplayMemberBinding="{Binding Number}" />
                                    <GridViewColumn Header="State" Width="50" DisplayMemberBinding="{Binding State}" />
                                </GridView>
                            </Setter.Value>
                        </Setter>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </ListView.Style>
    </ListView>
</Grid>

1 个答案:

答案 0 :(得分:1)

您不必通过将模板替换为使用不同属性创建控件的新嵌套实例的模板来设置控件的属性。在WPF中,ControlTemplate确定控件的显示方式,它不会创建控件。而是使用设置属性的样式设置属性。如果 更改ListView的模板,这就是你要做的。

以下是如何做到这一点的(当然,我建议不要将其命名为UserControl1):

UserControl1.xaml.cs

public partial class UserControl1 : UserControl
{
    public UserControl1()
    {
        InitializeComponent();
    }

    public IEnumerable ItemsSource
    {
        get { return (IEnumerable)GetValue(ItemsSourceProperty); }
        set { SetValue(ItemsSourceProperty, value); }
    }

    public static readonly DependencyProperty ItemsSourceProperty =
        DependencyProperty.Register(nameof(ItemsSource), typeof(IEnumerable), typeof(UserControl1),
            new PropertyMetadata(null));

    public ViewPurpose ViewPurpose
    {
        get { return (ViewPurpose)GetValue(ViewPurposeProperty); }
        set { SetValue(ViewPurposeProperty, value); }
    }

    public static readonly DependencyProperty ViewPurposeProperty =
        DependencyProperty.Register(nameof(ViewPurpose), typeof(ViewPurpose), typeof(UserControl1),
            new PropertyMetadata(ViewPurpose.None));
}

public enum ViewPurpose
{
    None,
    FoodPreference,
    ContactInfo,
    FredBarneyWilma
}

UserControl1.xaml

<UserControl 
    x:Class="WpfApp3.UserControl1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    xmlns:local="clr-namespace:WpfApp3"
    mc:Ignorable="d" 
    d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <ListView
            ItemsSource="{Binding ItemsSource, RelativeSource={RelativeSource AncestorType=UserControl}}"
            >
            <ListView.Style>
                <Style TargetType="ListView">
                    <Style.Triggers>
                        <DataTrigger 
                            Binding="{Binding ViewPurpose, RelativeSource={RelativeSource AncestorType=UserControl}}"
                            Value="FoodPreference"
                            >
                            <Setter Property="View">
                                <Setter.Value>
                                    <GridView>
                                        <GridViewColumn Header="Name" Width="120" DisplayMemberBinding="{Binding Name}" />
                                        <GridViewColumn Header="Food" Width="50" DisplayMemberBinding="{Binding Food}" />
                                    </GridView>
                                </Setter.Value>
                            </Setter>
                        </DataTrigger>
                        <DataTrigger 
                            Binding="{Binding ViewPurpose, RelativeSource={RelativeSource AncestorType=UserControl}}"
                            Value="ContactInfo"
                            >
                            <Setter Property="View">
                                <Setter.Value>
                                    <GridView>
                                        <GridViewColumn Header="Name" Width="120" DisplayMemberBinding="{Binding Name}" />
                                        <GridViewColumn Header="Number" Width="120" DisplayMemberBinding="{Binding Number}" />
                                        <GridViewColumn Header="State" Width="50" DisplayMemberBinding="{Binding State}" />
                                    </GridView>
                                </Setter.Value>
                            </Setter>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </ListView.Style>
        </ListView>
    </Grid>
</UserControl>

用法示例:

<StackPanel Orientation="Vertical">
    <local:UserControl1 
        ViewPurpose="FoodPreference"
        ItemsSource="{Binding SomeCollectionOfWhatever}" 
        />
    <local:UserControl1 
        ViewPurpose="ContactInfo"
        ItemsSource="{Binding DifferentCollectionOfWhatever}" 
        />
</StackPanel>

枚举是指定一组列的一个选项。你也可以给它一个列名的集合,或者由一些特殊字符(&#34; Name | Food | Gas | Lodging&#34;)分隔的单个字符串,它们将被拆分,然后在UserControl中做一些事情来创建基于此的列集合。

但是,如果您有两个或三个预定义的列集合,具有自定义宽度等等,这将快速而简单并完成工作。你不需要对这个过于聪明。