正确设置DataContext

时间:2014-11-18 11:53:34

标签: c# wpf xaml data-binding

即时构建一个拥有自己的ViewModel MyUserControl的UserControl MyUserControlViewModelMyUserControl包含6 VehicleSelectionBlock(V1,... V6)。 VehicleSelectionBlock是我制作的UserControl。它有3 RadioButtoncar, train, bus;所有属于enum类型Vehicle且属于同一群组VehicleGroup

我的目标是代表MyUserControl中的VehicleSelectionBlock MyUserControlViewModel个{}。

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

我的代码如下 - 一般来说,我对wpf和数据绑定有点新鲜。我几乎阅读了每一章in this tutorial,但有时仍然丢失。

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

TY

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

{{1}}

2 个答案:

答案 0 :(得分:0)

您可以直接在控件的代码隐藏中(在默认构造函数中)

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

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

答案 1 :(得分:0)

这是一个快速的解决方案。请记住,如果要为Vehicle枚举添加更多值,则需要更改代码。

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");
            }
        }
    }

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

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

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

每个项目也是UserControl。 VehicleControl用户控件只是一个显示RadioButons的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>

请注意,每个RadioButton都绑定到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>