0

im building a UserControl MyUserControl that has his own ViewModel MyUserControlViewModel. MyUserControl contains 6 VehicleSelectionBlock (V1, ... V6). VehicleSelectionBlock is a UserControl i've made. it has 3 RadioButton: car, train, bus; all are of enum type Vehicle and of the same GroupName VehicleGroup.

my goal is to represent each of MyUserControl's VehicleSelectionBlocks in MyUserControlViewModel.

to make my self clear: in MyUserControlViewModel i want to be able to know&change what RadioButton is checked in every one of the 6 VehicleSelectionBlock. i think my main problem is not the converter but rather the DataContex - i'm not sure how to set it correctly for each of the controllers. iv'e tried Binding (which is the obvious solution). i tried reading here, here , and here. unfortunately neither one helped my acheive my goal.

my code is below - im kinda new to wpf and data binding in generally. i've read almost every chapter in this tutorial but still lost sometimes.

please help me get through this and understand better the DataContex concept.

ty

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}
    }
Community
  • 1
  • 1
zook2005
  • 195
  • 2
  • 7
  • Too much code and details... I'm trying to dig through and understand what you question is. Are you trying to find a way to set your ViewModel as the DataContext of your UserControl? – Moti Azu Nov 18 '14 at 11:59
  • @MotiAzu When the user check's one of the radioButtons, say V1->car - it will be reflected in MyUserContlorViewModel (i dont know how to do that + i dont know what data structure i shoul use to keep that information (which radioButton is checked on each of the VehicleSelectionBlocks that are contained in MyUserContlor) – zook2005 Nov 18 '14 at 12:24

2 Answers2

0

You can do directly in the code-behind of your control (in the default constructor)

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

You can also do that in XAML (http://msdn.microsoft.com/en-us/library/ms746695(v=vs.110).aspx) declaration, as you wish.

v20100v
  • 696
  • 4
  • 11
  • i dont want to create a new instance of MyUserContlorViewModel: MyUserContlor has 6 VehicleSelectionBlocks and the information about what is checked in each one is running from MyUserContlor through it's ViewModel (of type MyUserContlorViewModel) – zook2005 Nov 18 '14 at 12:32
  • Sorry, I misunderstood... have a look about master-detailled pattern (http://msdn.microsoft.com/en-us/library/ms742531(v=vs.110).aspx) – v20100v Nov 18 '14 at 12:38
  • i've read it twice but i'm not sure what is it that you want me to see: is it "You set the Path property of the binding to specify which level of data you want the ListBox to display."? that is nice but i still not sure what data structure to use and how to set the right dataContex – zook2005 Nov 18 '14 at 12:52
0

here is a quick solution. keep in mind that the code needs to change if you want to add more values to your Vehicle enum.

the MyUserControlViewModel.cs file

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

this will expose your 6 items. They could be more. As a result they will be displayed in an ItemsControl, as you will see later.

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

instances of VehicleViewModel will contain your radio selection using 3 bool properties. this is the solution disadvantage. If you want more values you'll have to add more properties. you can see this inherits ViewModelBase. ViewModelBase just implements INPC so i'm not going to put it here. ViewModelBase also exposes the OnChange method that triggers the INPC event.

displaying the list can be done in your MyUserControl by using an ItemsControl like below.

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

each item is also a UserControl. The VehicleControl user control is just a StackPanel that displays the RadioButons. This can be seen below.

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

please notice that each RadioButton is bound to one of the 3 properties in the VehicleViewModel instance. Once you press your button you should have all the selections recorded. if you want you could have a function that returns an enum value by analysing the 3 bool properties if that is what you need.

the best solution will be to get rid of the radio buttons and replace them with combo boxes. in this way you can change the enum members and everything will continue to work without changing anything else. this might look as below.

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

and for the view:

<ItemsControl ItemsSource="{Binding VehicleArr}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <ComboBox ItemsSource="{Binding Options}" 
                              SelectedItem="{Binding SelectedOption, Mode=TwoWay}"/>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
flo_badea
  • 774
  • 5
  • 8
  • thank you for your comment. this looks good but a bit confusing. i'm wanna get rid of the radio buttons as you've suggested and use the second solution. i've just tried it: I can't seem to understand how does the view of MyUserControl (meaning the MyUserControl.xaml file) will know MyUserControlViewModel.cs and its members (specifically VehicleArr (in this line of code : ). this solution in it's current form does not work for me. maybe I need to set the data context of MyUserControl to point at MyUserControlViewModel? – zook2005 Nov 20 '14 at 07:15
  • aa. yes. i forgot to set the data context for the MyUserControl to a new instance of MyUserControlViewModel. in my sample i created the instance directly in the ctor of MyUserControl and assigned that instance to the DataContext. The array is exposed in the view-model as a property named VehicleArr and the items control databinds to this property. After this each element in the array will be automatically set (by the databinding engine) as the data context for each item in the list. Each item is of type VehicleViewModel – flo_badea Nov 20 '14 at 08:07