正确设置数据Contex

本文关键字:Contex 置数据 | 更新日期: 2023-09-27 18:30:42

im 构建一个具有自己的 ViewModel MyUserControlViewModel 的用户控件MyUserControlMyUserControl包含 6 个VehicleSelectionBlock(V1、...V6)。 VehicleSelectionBlock是我制作的用户控件。它有3个RadioButtoncar, train, bus;所有这些都是enum类型 Vehicle 并且具有相同的组名VehicleGroup

我的目标是在MyUserControlViewModel中代表MyUserControl的每一个VehicleSelectionBlock

让我自己清楚:MyUserControlViewModel我希望能够知道和更改在 6 VehicleSelectionBlock中的每一个中检查RadioButton的内容。 我认为我的主要问题不是转换器,而是 DataContex - 我不确定如何为每个控制器正确设置它。iv'e 尝试了绑定(这是显而易见的解决方案)。我试着在这里、这里和这里阅读。不幸的是,没有人帮助我实现我的目标。

我的代码在下面 - 我一般对 WPF 和数据绑定有点陌生。 我几乎读过本教程的每一章,但有时仍然会迷失方向。

请帮助我解决这个问题并更好地理解DataContex概念。

MyUserContlor.xaml.cs:

    namespace Project01
    {
        /// <summary>
        /// Interaction logic for MyUserContlor.xaml
        /// </summary>
        public partial class MyUserContlor : UserControl
        {
            public MyUserContlorViewModel ViewModel { get; set; }
            public MyUserContlor()
            {
                ViewModel = new MyUserContlorViewModel();
                InitializeComponent();
                this.DataContext = ViewModel;
            }
            private void BtnImReady_OnClick(object sender, RoutedEventArgs e)
            {
                //this code is irrelevant to the question
                throw NotImplementedException();
            }
        }
    }

MyUserContlor.xaml:

    <UserControl x:Class="Project01.MyUserContlor"
         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:loc="clr-namespace:Project01"
         mc:Ignorable="d" 
         HorizontalContentAlignment="Center" VerticalContentAlignment="Center">
         <Viewbox Stretch="Uniform">
            <StackPanel>
                <loc:VehicleSelectionBlock Name="V1"/>
                <loc:VehicleSelectionBlock Name="V2"/>
                <loc:VehicleSelectionBlock Name="V3"/>
                <loc:VehicleSelectionBlock Name="V4"/>
                <loc:VehicleSelectionBlock Name="V5"/>
                <loc:VehicleSelectionBlock Name="V6"/>
                <Button x:Name="BtnImReady" Click="BtnImReady_OnClick">Im Ready!</Button>
            </StackPanel>
         </Viewbox>  
    </UserControl> 

MyUserContlorViewModel.cs:

    namespace Project01 
    {
        public class MyUserContlorViewModel : INotifyPropertyChanged
        {
            public MyUserContlorViewModel()
            {
                VehicleArr = new MyViewModel_Vehicle[6];
                PropertyChanged+=MyUserControlViewModel_PropertyChanged;
            }
            public MyViewModel_Vehicle[] VehicleArr;
            public event PropertyChangedEventHandler PropertyChanged;
            public PropertyChangedEventHandler GetPropertyChangedEventHandler() { return PropertyChanged; }
            private void MyUserControlViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
            {
                //might be useful
                throw NotImplementedException();
            }
        }
        //this class should represent a VehicleSelectionBlock
        public class MyViewModel_Vehicle
        {
            public Vehicle VehicleSelected {get; set;}
            MyViewModel_Vehicle(){}
            MyViewModel_Vehicle(Vehicle v){ VehicleSelected = v;}
        }
    }

VehicleSelectionBlock.xaml:

    <UserControl x:Class="Project01.VehicleSelectionBlock"
                 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:Project01"
                 mc:Ignorable="d" 
                 d:DesignHeight="300" d:DesignWidth="300">
        <Grid DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}">      
            <Border VerticalAlignment="Center" HorizontalAlignment="Center" Background="GhostWhite"
                BorderBrush="Gainsboro" BorderThickness="1">
                <StackPanel >                   
                    <Label Content="{Binding Name}"
                        FontWeight="Bold" HorizontalContentAlignment="Center"></Label>
                    <RadioButton GroupName="VehicleGroup" >car</RadioButton>
                    <RadioButton GroupName="VehicleGroup">train</RadioButton>
                    <RadioButton GroupName="VehicleGroup" IsChecked="True">bus</RadioButton>
                </StackPanel>
            </Border>
        </Grid>
    </UserControl>

VehicleSelectionBlock.xaml.cs:

    namespace Project01
    {
        /// <summary>
        /// Interaction logic for VehicleSelectionBlock.xaml
        /// </summary>
        public partial class VehicleSelectionBlock : UserControl
        {
            public VehicleSelectionBlock()
            {
                InitializeComponent();
            }
            public VehicleSelectionBlock(String name)
            {
                name = Name;
                InitializeComponent();
            }
            public static readonly DependencyProperty NameProperty = DependencyProperty.Register(
                "Name", typeof (String), typeof (VehicleSelectionBlock), new PropertyMetadata(default(String)));
            public String Name
            {
                get { return (String) GetValue(NameProperty); }
                set { SetValue(NameProperty, value); }
            }
        }
        public enum Vehicle { Car, Train, Bus}
    }

正确设置数据Contex

这里有一个快速的解决方案。 请记住,如果要向载具枚举添加更多值,则需要更改代码。

MyUserControlViewModel.cs 文件

public class MyUserControlViewModel
    {
        public MyUserControlViewModel()
        {
            VehicleArr = new VehicleViewModel[6];
            for (int i = 0; i < 6;i++ )
                VehicleArr[i] = new VehicleViewModel();
        }
        public VehicleViewModel[] VehicleArr { get; set; }
    }

这将公开您的 6 个项目。他们可能会更多。因此,它们将显示在 ItemsControl 中,稍后您将看到。

public class VehicleViewModel:ViewModelBase
    {
        private bool isCar, isTrain, isBus;
        public bool IsCar
        {
            get { return isCar; }
            set
            {
                if (isCar == value) return;
                isCar = value;
                OnChanged("IsCar");
            }
        }
        public bool IsTrain
        {
            get { return isTrain; }
            set
            {
                if (isTrain == value) return;
                isTrain = value;
                OnChanged("IsTrain");
            }
        }
        public bool IsBus
        {
            get { return isBus; }
            set
            {
                if (isBus == value) return;
                isBus = value;
                OnChanged("IsBus");
            }
        }
    }

车辆视图模型的实例将使用 3 个布尔属性包含您的无线电选择。 这是解决方案的缺点。如果你想要更多值,则必须添加更多属性。你可以看到它继承了 ViewModelBase。ViewModelBase只是实现了INPC,所以我不打算把它放在这里。ViewModelBase 还公开了触发 INPC 事件的 OnChange 方法。

可以在 MyUserControl 中使用如下所示的 ItemsControl 完成列表的显示。

<ItemsControl ItemsSource="{Binding VehicleArr}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <loc:VehicleControl />
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>

每个项也是一个用户控件。VehicleControl 用户控件只是一个显示 RadioButon 的 StackPanel。这可以在下面看到。

<StackPanel Orientation="Horizontal">
            <RadioButton Content="Car" Margin="5" VerticalAlignment="Center" IsChecked="{Binding Path=IsCar, Mode=TwoWay}"/>
            <RadioButton Content="Train" Margin="5" VerticalAlignment="Center" IsChecked="{Binding Path=IsTrain, Mode=TwoWay}"/>
            <RadioButton Content="Bus" Margin="5" VerticalAlignment="Center" IsChecked="{Binding Path=IsBus, Mode=TwoWay}"/>
        </StackPanel>

请注意,每个单选按钮都绑定到 VehicleViewModel 实例中的 3 个属性之一。按下按钮后,您应该记录所有选择。如果需要,您可以拥有一个函数,该函数通过分析 3 个 bool 属性来返回枚举值(如果需要)。

最好的解决方案是摆脱单选按钮并用组合框替换它们。 通过这种方式,您可以更改枚举成员,并且所有内容将继续工作而无需更改任何其他内容。 这可能如下所示。

public class VehicleViewModel:ViewModelBase
    {
        private Vehicle selOption;
        private readonly Vehicle[] options;
        public VehicleViewModel()
        {
            this.options = (Vehicle[])Enum.GetValues(typeof(Vehicle));
        }
        public Vehicle[] Options { get { return options; } }
        public Vehicle SelectedOption
        {
            get { return selOption; }
            set
            {
                if (selOption == value) return;
                selOption = value;
                OnChanged("SelectedOption");
            }
        }
    }

对于视图:

<ItemsControl ItemsSource="{Binding VehicleArr}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <ComboBox ItemsSource="{Binding Options}" 
                              SelectedItem="{Binding SelectedOption, Mode=TwoWay}"/>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>

可以直接在控件的代码隐藏中执行此操作(在默认构造函数中)

public VehicleSelectionBlock()
{
    InitializeComponent();
    this.DataContext = new MyUserContlorViewModel ();
}

您也可以根据需要在 XAML (http://msdn.microsoft.com/en-us/library/ms746695(v=vs.110).aspx) 声明中执行此操作。