WPF Window (or do I mean View) Navigation (or do I mean switching, opening, closing…) part 3 #wpf

Following on from my post WPF Window (or do I mean View) Navigation (or do I mean switching, opening, closing…) part 2 I have finally put together the view switching part, here goes…

First I added two User Control (WPF) items (for now I’m going to keep these in a new folder called SubViews) and a MvvmViewModel (WPF) for each:
Nav\Views\SubViews\UC1.xaml + .cs
Nav\Views\SubViews\UC2.xaml + .cs
Nav\ViewModels\UC1ViewModel.cs
Nav\ViewModels\UC2ViewModel.cs

Next I added some dependency properties to my ViewModels using the mvvminpc snippet.
Starting with UC1ViewModel, I added Val1 of type String:

        /// <summary>
        /// The <see cref="Val1" /> property's name.
        /// </summary>
        public const string Val1PropertyName = "Val1";

        private string _val1 = "";

        /// <summary>
        /// Sets and gets the Val1 property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public string Val1
        {
            get
            {
                return _val1;
            }

            set
            {
                if (_val1 == value)
                {
                    return;
                }

                RaisePropertyChanging(Val1PropertyName);
                _val1 = value;
                RaisePropertyChanged(Val1PropertyName);
            }
        }

Similarly I added Val2 to UC2ViewModel.
These are just something to bind to and display.

Next I added a dependency property of type UserControl and called UC to AnotherViewModel, I will bind to this and change this to swap the UserControl that is displayed:

        /// <summary>
        /// The <see cref="UC" /> property's name.
        /// </summary>
        public const string UCPropertyName = "UC";

        private UserControl _uc = null;

        /// <summary>
        /// Sets and gets the UC property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public UserControl UC
        {
            get
            {
                return _uc;
            }

            set
            {
                if (_uc == value)
                {
                    return;
                }

                RaisePropertyChanging(UCPropertyName);
                _uc = value;
                RaisePropertyChanged(UCPropertyName);
            }
        }

Of course next I needed the UserControl instances and commands to swap them, I added them like so:

        private UC1 _uc1;
        private UC2 _uc2;
        public RelayCommand Swap1a { get; set; }
        public RelayCommand Swap1b { get; set; }

And in constructor:

            _uc1 = new UC1() { DataContext = new UC1ViewModel() { Val1 = "AVM uc 1a" } };
            _uc2 = new UC2() { DataContext = new UC2ViewModel() { Val2 = "AVM uc 1b" } };

            Swap1a = new RelayCommand(() => { UC = _uc1; });
            Swap1b = new RelayCommand(() => { UC = _uc2; });

Next up was to add code to ViewModelLocator to add design and blendability.
Using the mvvmlocatorproperty snippet I added a property for use when designing the UserControls, notice I don’t use the IoC container in this case:

        /// <summary>
        /// Gets the UC1VMDesignTime property.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
            "CA1822:MarkMembersAsStatic",
            Justification = "This non-static member is needed for data binding purposes.")]
        public UC1ViewModel UC1VMDesignTime
        {
            get
            {
                if (ViewModelBase.IsInDesignModeStatic)
                {
                    return new UC1ViewModel() { Val1 = "Designing val1..." };
                }
                else
                {
                    return null;
                }
            }
        }

        /// <summary>
        /// Gets the UC2VMDesignTime property.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
            "CA1822:MarkMembersAsStatic",
            Justification = "This non-static member is needed for data binding purposes.")]
        public UC2ViewModel UC2VMDesignTime
        {
            get
            {
                if (ViewModelBase.IsInDesignModeStatic)
                {
                    return new UC2ViewModel() { Val2 = "Designing val2..." };
                }
                else
                {
                    return null;
                }
            }
        }

I also changed Another so that at design time there was data in the UC:

        /// <summary>
        /// Gets the Another property.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
            "CA1822:MarkMembersAsStatic",
            Justification = "This non-static member is needed for data binding purposes.")]
        public AnotherViewModel Another
        {
            get
            {
                AnotherViewModel avm = ServiceLocator.Current.GetInstance<AnotherViewModel>();

                if (ViewModelBase.IsInDesignModeStatic)
                {
                    avm.UC = new UC1() { DataContext = UC1VMDesignTime };
                }

                return avm;
            }
        }

Finally I wired it all up in the xaml files.
UC1.xaml:

d:DataContext="{Binding UC1VMDesignTime, Source={StaticResource Locator}}"
....
<StackPanel>
	    	<Label Content="uc1" Height="28" Margin="8,10,0,0" x:Name="label1" VerticalAlignment="Top" HorizontalAlignment="Left" Width="36.803" />
	    	<TextBox x:Name="txt1" Margin="83,0,111,0" TextWrapping="Wrap" Text="{Binding Val1}" VerticalAlignment="Top" Height="49.96"/>
	    	<Label x:Name="lbl1" Content="{Binding Val1}" Margin="68,0,71.007,58.04" VerticalAlignment="Bottom" Height="44.96"/>
		</StackPanel>

UC2.xaml:

d:DataContext="{Binding UC2VMDesignTime, Source={StaticResource Locator}}"
....
<StackPanel>
    		<Label Content="uc2" HorizontalAlignment="Left" Height="35.96" Width="54" />
    		<Label Content="{Binding Val2}" Height="39.96" Margin="78,0,40,0" />
        </StackPanel>

AnotherWindow.xaml:

        <ContentControl x:Name="ContentControl" Content="{Binding UC}" Margin="177.5,82,110.5,0" Height="255" Width="464" VerticalAlignment="Top">
        </ContentControl>
        <Button Content="Swap to 1a" HorizontalAlignment="Left" Margin="0,0,0,124.04" VerticalAlignment="Bottom" Width="75" Command="{Binding Swap1a}"/>
        <Button Content="Swap to 1b" HorizontalAlignment="Left" Margin="0,0,0,98.08" VerticalAlignment="Bottom" Width="75" Command="{Binding Swap1b}"/>

Note that d:DataContext is ignored except at design time, so we have the ability to chose a design time model and I have coded those models so that they are null when not in design time anyway. I really like how that bit turned out, it’s really nice to have something you can design later (or pass onto a designer), there is nothing worse than a broken view that you can’t use in VisualStudio or Blend.

Leave a comment