Custom dependency property binding - c#

I got some problem with custom dependency property binding.
We have:
Custom user control with one dependency property and binding to self:
<UserControl x:Class="WpfApplication1.SomeUserControl"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" DataContext="{Binding RelativeSource={RelativeSource Mode=Self}}">
<Grid>
<Label>
<Label.Template>
<ControlTemplate>
<Label Content="{Binding MyTest}"/>
</ControlTemplate>
</Label.Template>
</Label>
</Grid>
... and code of control:
public partial class SomeUserControl : UserControl
{
public SomeUserControl()
{
InitializeComponent();
}
public static readonly DependencyProperty MyTestProperty = DependencyProperty.Register("MyTest", typeof(int), typeof(SomeUserControl));
public int MyTest
{
get { return (int)GetValue(MyTestProperty); }
set { SetValue(MyTestProperty, value); }
}
}
I trying to use this control with binding to some simple property of simple model class:
<UserControl x:Class="WpfApplication1.AnotherUserControl"
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:wpfApplication1="clr-namespace:WpfApplication1"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" DataContext="{Binding RelativeSource={RelativeSource Mode=Self}}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<wpfApplication1:SomeUserControl MyTest="{Binding Path=Model.MyNum}" Grid.Column="0"/>
<Label Content="{Binding Path=Model.MyNum}" Grid.Column="1"/>
</Grid>
... with code
public partial class AnotherUserControl : UserControl
{
public MyModel Model { get; set; }
public AnotherUserControl()
{
Model = new MyModel();
Model.MyNum = 1231;
InitializeComponent();
}
}
... and model:
public class MyModel:INotifyPropertyChanged
{
private int _myNum;
public int MyNum
{
get { return _myNum; }
set { _myNum = value; OnPropertyChanged("MyNum");}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
But binding is not working. Stack have no errors in compilation. Now, binding working with statndart wpf label control (with same model), and don't working with my custom control and custom property. Please help me to understand reasons of this problem and solve it;
Thanks)

You should use ElementName Binding in your SomeUserControl.
<UserControl x:Class="WpfApplication1.SomeUserControl"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
x:Name="uc">
<Grid>
<Label>
<Label.Template>
<ControlTemplate>
<Label Content="{Binding MyTest, ElementName=uc}"/>
</ControlTemplate>
</Label.Template>
</Label>
</Grid>
here some more information why you should not set the datacontext of a usercontrol to self/this.
Data Binding in WPF User Controls

You will need to update your binding as below i.e you will have to bind to the ancestor usercontrol property using RelativeSource as you are setting the DataContext of child usercontrol explicitly, default binding will search for path inside the child userControl's DataContext.
<wpfApplication1:SomeUserControl MyTest="{Binding Path=DataContext.Model.MyNum, RelativeSource={RelativeSource FindAncestor={x:Type UserControl}}}" Grid.Column="0"/>

Related

C# WPF use properties from different usercontrol as Command parameter

I have a problem with referencing properties or elements on different user controls in my MVVM WPF application.
Edit: Reduced code to a MCVE (hopefully). Also removed PropertyChanged Events to reduce code.
TLDR
I split up all my MainWindow.xaml elements to different user controls
In the menubar (one control) I want to fire a ICommand to save a Settings object (which is the datacontext of the MainWindow
For the method that is called in the Command I need a value from a completely different UserControl that is neither parent nor child of the menubar
How can I retrieve the value from the PasswordBox in a MVVM approach (most preferably as the CommandParameter on the MenuBar)?
View
MainWindow.xaml
<Window x:Class="WpfApp1.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:ViewModel="clr-namespace:WpfApp1.ViewModel"
xmlns:View="clr-namespace:WpfApp1.View"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<ViewModel:SettingsViewModel/>
</Window.DataContext>
<StackPanel>
<DockPanel>
<View:MenuBar DataContext="{Binding}"/>
</DockPanel>
<TabControl>
<TabItem Header="Tab1" DataContext="{Binding AppSettings}">
<View:TabItemContent/>
</TabItem>
</TabControl>
</StackPanel>
</Window>
The MenuBar which is supposed to bind on ICommand properties on the ViewModel (the DataContext of MainWindow).
MenuBar.xaml
<UserControl x:Class="WpfApp1.View.MenuBar"
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:WpfApp1.View"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" >
<Menu DockPanel.Dock="Top" Height="Auto" >
<!-- In the command I need a reference to the password on the tabItem -->
<MenuItem Name="SaveItem" Height="Auto" Header="Save"
Command="{Binding SaveCommand}"
CommandParameter="Binding RelativeSource=
{RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"/>
</Menu>
</UserControl>
TabItemContent.xaml
<UserControl x:Class="WpfApp1.View.TabItemContent"
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:WpfApp1.View"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
DataContext="{Binding PasswordSettings}">
<Grid>
<PasswordBox Height="25" Width="100" />
</Grid>
</UserControl>
In TabItemControl.xaml.cs I tried to introduce a Dependency Property and set the value to the datacontext of the control.
ViewModel
SettingViewModel.cs
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;
using WpfApp1.Commands;
using WpfApp1.Model;
namespace WpfApp1.ViewModel
{
public class SettingsViewModel
{
public Settings AppSettings { get; private set; } = new Settings();
public ICommand SaveCommand { get; private set; }
public SettingsViewModel()
{
SaveCommand = new DelegateCommand( SaveSettings );
// load settings and call
// OnPropertyChanged( "AppSettings" );
}
public void SaveSettings( object o = null )
{
if( o is string encryptedPass )
{
// get the password and save it to AppSettings object
}
// call save method on settings
}
}
}
Model (Setting classes)
AppSetting.cs (encapsulating all the setting objects from the other tabs)
namespace WpfApp1.Model
{
public class Settings
{
public PasswordSettings PwSettings { get; set; } = new PasswordSettings();
}
}
PasswordSettings.cs
namespace WpfApp1.Model
{
public class PasswordSettings
{
public string EncryptedPass { get; set; }
}
}
and finally the DelegateCommand implementation in
DelegateCommand.cs see this gist

In a WPF application using Visual Studio, how can I bind a usercontrol to a usercontrol variable?

I'm not sure if I asked the question correctly, because I am not finding the answer by searching the internet. I am creating a wizard window. I have a window that has a title at the top and buttons at the bottom that will stay there throughout changing the pages. So in the xaml.cs for the window, I have a list of UserControls that will contain all of the views for the wizard. I also have a property/field that holds the current view. I want to create a xaml UserControl tag that binds to the current view property. It should change when I change the current view property (I have already implemented the INotifyChanged interface). The current view property will be changed by c#. Here is the code that I have(simplified to show whats needed), and when I run it nothing shows in the view area:
WizardWindow.xaml:
<Window x:Class="WizardWindow.WizardWindow"...>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="70"/>
<RowDefinition Height="*"/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<TextBlock Text="Wizard Title"/>
</Grid>
<Grid Grid.Row="1">
<Border Name="WizardWindowPageContent" Margin="5" BorderBrush="Black" BorderThickness="1">
<!--This is what I have tried but isn't working -->
<UserControl Content="{Binding CurrentView}" />
</Border>
</Grid>
<Grid Grid.Row="2" Name="WizardButtons">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0">Cancel</Button>
<Button Grid.Column="2">Back</Button>
<Button Grid.Column="3">Next</Button>
</Grid>
</Grid>
</Window>
WizardWindow.xaml.cs:
using System;
...
using System.Windows.Controls;
namespace WizardWindow
{
public partial class WizardWindow : Window, INotifyPropertyChanged
{
// List of views in the config window
private List<UserControl> views;
// Current view showing in the window
private UserControl currentView;
public UserControl CurrentView
{
get { return currentView; }
set
{
currentView = value;
OnPropertyChanged("CurrentView");
}
}
// Used to keep track of the view index
private int viewIndex;
public WizardWindow ()
{
InitializeComponent();
// Set the screen to the center of the screen
WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen;
views = new List<UserControl>();
views.Add(new FirstWizardPage(this));
viewIndex = 0;
CurrentView = views[viewIndex];
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string info)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(info));
}
}
}
}
FirstWizardPage.xaml:
<!-- This should show up in the window -->
<UserControl x:Class="WizardWindow.FirstWizardPage" ... >
<Grid>
<TextBlock>Lorem Ipsum ...</TextBlock>
</Grid>
</UserControl>
FirstWizard.xaml.cs:
using System.Windows.Controls;
namespace WizardWindow
{
public partial class FirstWizardPage : UserControl
{
public FirstWizardPage(WizardWindow window)
{
InitializeComponent();
}
}
}
The given possible duplicate, Window vs Page vs UserControl for WPF navigation?
, is a good solution if I wanted to rewrite my program. However, it is not a solution to my exact problem. Someone else might have a similar problem and need this solution.
you need to use this:
<ContentControl x:Name="MyView"
Content="{Binding CurrentView}" />
It should work for you, but it's not MVVM.
You must binding on property DataContext of your user control, if you want to use MVVM.
I Will show little example for you:
It is MainWindow.xaml:
<Window x:Class="WpfApp2.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:WpfApp2"
xmlns:cefSharp="clr-namespace:CefSharp.Wpf;assembly=CefSharp.Wpf"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<local:Foo DataContext="{Binding UserControlViewModel}"/>
</Grid>
It is MainWindow.xaml.cs:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
}
It is MainWindowViewModel.cs:
public class MainWindowViewModel
{
public MainWindowViewModel()
{
UserControlViewModel = new UserControlViewModel{ Name = "Hello World" };
}
public UserControlViewModel UserControlViewModel { get; }
}
It is Foo.xaml:
<UserControl x:Class="WpfApp2.Foo"
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:WpfApp2"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TextBlock Text="{Binding Name}"/>
</Grid>
It is FooUsercontrolViewModel.cs
public class FooUserControlViewModel
{
public string Name { get; set; }
}

How to manage User Control in List Box using MVVM?

i know that there are many question of this, but i don't find the correct answer, or i don't understand the correct way to solve.
I have my list box in the MainWindows, it is populated by custom object (FooObjClass).
<ListBox x:Name="FooListBox">
<ListBox.ItemTemplate>
<DataTemplate>
<fooNameSpace:FooObjView/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
the FooObjView is a User Control
<UserControl x:Class="PLCS7_TEST.SmartObjRecognize.SmartObjView"
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:Test.FooObjRecognize"
mc:Ignorable="d" Width="Auto">
<UserControl.DataContext>
<local:FooViewModel/>
</UserControl.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Path=FooObjprop.Name}"/>
<TextBlock Grid.Column="1" Text="{Binding Path=FooObjprop.Type}">
</Grid>
and this is my FooViewModel
class FooViewModel : ObservableObject
{
private FooObjClass fooObjmember;
public FooObjClass FooObjprop
{
get { return fooObjmember; }
set
{
this.fooObjmember= value;
base.RaisePropertyChanged("FooObjprop");
}
}
}
the FooObjClass is a normal class and the ObservableObject class is this:
public abstract class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
var handler = this.PropertyChanged;
if (handler != null)
{
handler(this, e);
}
}
protected void RaisePropertyChanged<T>(Expression<Func<T>> propertyExpresssion)
{
var propertyName = PropertySupport.ExtractPropertyName(propertyExpresssion);
this.RaisePropertyChanged(propertyName);
}
protected void RaisePropertyChanged(String propertyName)
{
VerifyPropertyName(propertyName);
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
public void VerifyPropertyName(String propertyName)
{
// verify that the property name matches a real,
// public, instance property on this Object.
if (TypeDescriptor.GetProperties(this)[propertyName] == null)
{
Debug.Fail("Invalid property name: " + propertyName);
}
}
}
Now, what i can do for pass the item object of the list box (it's a FooObjClass ) to the last FooModelView? I have to use the Dependency Property? But How? I tryed and i readed all, but i don't find solution
With this piece of code i would to pass the item object of the list box to the FooViewModel
<UserControl.DataContext>
<local:FooViewModel fooObjmember="{Databinding}"/>
</UserControl.DataContext>
but the fooObjmember is not a dependency property, and when i tried to create a dependency property it's the same
Thank you and sorry for my english ;)
Welcome,
Here are your errors:
You're not specifying the binding for a ListBoxItem
You are instantiating another model in the UserControl
You are binding to field fooObjmember, only properties are allowed
Here's a working example:
Window XAML
<Window x:Class="WpfApplication1.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:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<!--<ListBox ItemsSource="{Binding}"> binds to DataContext property-->
<ListBox ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<local:MyUserControl DataContext="{Binding}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
Window code
using System.Collections.Generic;
namespace WpfApplication1
{
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
DataContext = new List<MyModel>
{
new MyModel {Number = 1, Value = "one"},
new MyModel {Number = 2, Value = "two"}
};
}
}
}
User control XAML
<UserControl x:Class="WpfApplication1.MyUserControl"
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:WpfApplication1"
mc:Ignorable="d" d:DataContext="{d:DesignInstance local:MyModel}"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Border Padding="5" BorderBrush="Red" BorderThickness="1">
<StackPanel>
<TextBlock Text="{Binding Number}" />
<TextBlock Text="{Binding Value}" />
</StackPanel>
</Border>
</Grid>
</UserControl>
User control code
namespace WpfApplication1
{
public partial class MyUserControl
{
public MyUserControl()
{
InitializeComponent();
}
}
}
Model code
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace WpfApplication1
{
internal class MyModel : INotifyPropertyChanged
{
private int _number;
private string _value;
public int Number
{
get { return _number; }
set
{
if (value == _number) return;
_number = value;
OnPropertyChanged();
}
}
public string Value
{
get { return _value; }
set
{
if (value == _value) return;
_value = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Result
Now adjust that to your needs :D

Multiple Instances of UserControl not Binding

Hi I tried to create a simple UserControl with TestValue1 and TestValue2 Properties and Created two Instances of it in a stack panel. But what is happening is that only the Last Control is storing the values in the User Control the rest have just blank values.
Here is the XAML code where they are used:
<userControls:TestControl TestValue1="Vasu" TestValue2="Mahesh"></userControls:TestControl>
<userControls:TestControl TestValue1="Test" TestValue2="Testing2"></userControls:TestControl>
Here is the UserControl's XAML and Code Behind:
Code Behind:
public sealed partial class TestControl : UserControl
{
public TestControl()
{
this.InitializeComponent();
this.DataContext = this;
}
public readonly DependencyProperty TestValue1DependencyProperty = DependencyProperty.Register("TestValue1", typeof(string), typeof(TestControl), new PropertyMetadata(default(string)));
public string TestValue1
{
get
{
return (string)GetValue(TestValue1DependencyProperty);
}
set
{
SetValue(TestValue1DependencyProperty, value);
}
}
public readonly DependencyProperty TestValue2DependencyProperty = DependencyProperty.Register("TestValue2", typeof(string), typeof(TestControl), new PropertyMetadata(default(string)));
public string TestValue2
{
get
{
return (string)GetValue(TestValue2DependencyProperty);
}
set
{
SetValue(TestValue2DependencyProperty, value);
}
}
}
XAML File:
<UserControl
x:Name="TestControlName"
x:Class="downloader.UserControls.TestControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:downloader.UserControls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<Grid>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding TestValue1 , ElementName=TestControlName}"></TextBlock>
<TextBlock Text="{Binding TestValue2 , ElementName=TestControlName}"></TextBlock>
</StackPanel>
</Grid>
</UserControl>
Solved!
The Trick was with the DataContext :
in the XAML of the UserControl I set up:
<UserControl
x:Name="TestControlName"
x:Class="downloader.UserControls.TestControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:downloader.UserControls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<Grid x:Name="LayoutRootGrid">
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding TestValue1}"></TextBlock>
<TextBlock Text="{Binding TestValue2}"></TextBlock>
</StackPanel>
</Grid>
</UserControl>
And in C# Code Behind:
//this.DataContext = this;
LayoutRootGrid.DataContext = this;
Probably because the DataContext was being Overridden by this ... Since DataContext is inherited from the Parent , by using the old line it was no longer inheriting from its parent.
So I gave the inner element the data context of the code behind which allowed the UserControl to inherit the DataContext of the Parent (i.e. from where it is called)

Changing ContentControl's Content from another view

I have question how to control views in content control. I have two views where are buttons and I want that click in one of this buttons will occur change of view to second. I'm using MVVM and my problem is that I don't know how change ViewModel binded to ContentControl. Maybe my code tell you more than I can clarify:
// Main window view
<Window x:Class="ContentControlTestApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
DataContext="{Binding Main, Source={StaticResource Locator}}">
<Grid>
<ContentControl Content="{Binding CurrentViewModel}"/>
</Grid>
</Window>
// Main window view model
public class MainViewModel : ViewModelBase
{
private ViewModelBase _currentViewModel;
public ViewModelBase CurrentViewModel
{
get { return _currentViewModel; }
set
{
_currentViewModel = value;
RaisePropertyChanged("CurrentViewModel");
}
}
private ViewModelLocator Locator
{
get
{
return App.Current.Resources["Locator"] as ViewModelLocator;
}
}
public MainViewModel()
{
CurrentViewModel = Locator.FirstControl;
}
}
//App.xaml
<Application x:Class="ContentControlTestApp.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" StartupUri="MainWindow.xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" d1p1:Ignorable="d" xmlns:d1p1="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:vm="clr-namespace:ContentControlTestApp.ViewModel" xmlns:view="clr-namespace:ContentControlTestApp.View">
<Application.Resources>
<vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True" />
<DataTemplate DataType="{x:Type vm:FirstControlViewModel}">
<view:FirstControlView></view:FirstControlView>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:SecondControlViewModel}">
<view:SecondControlView></view:SecondControlView>
</DataTemplate>
</Application.Resources>
</Application>
//First Control View
<UserControl x:Class="ContentControlTestApp.View.FirstControlView"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<StackPanel>
<Label Content="First Control" />
<Button Content="Switch to second control" Command="{Binding SwitchToSecondControlCommand}"/>
</StackPanel>
</Grid>
</UserControl>
//First Control view model
public class FirstControlViewModel:ViewModelBase
{
private RelayCommand _switchToSecondControlCommand;
public ICommand SwitchToSecondControlCommand
{
get
{
return _switchToSecondControlCommand ??
(_switchToSecondControlCommand = new RelayCommand(SwitchToSecondControlExecute));
}
}
private void SwitchToSecondControlExecute()
{
//I don't know what to do here
}
}
//Second Control View
<UserControl x:Class="ContentControlTestApp.View.SecondControlView"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<StackPanel>
<Label Content="Second Control" />
<Button Content="Switch to first control" Command="{Binding SwitchToFirstControlCommand}"/>
</StackPanel>
</Grid>
</UserControl>
//Second Control view model
public class SecondControlViewModel:ViewModelBase
{
private RelayCommand _switchToFirstControlCommand;
public ICommand SwitchToFirstControlCommand
{
get
{
return _switchToFirstControlCommand ??
(_switchToFirstControlCommand = new RelayCommand(SwitchToSecondControlExecute));
}
}
private void SwitchToSecondControlExecute()
{
//I don't know what to do here
}
}
//ViewModelLocator
public class ViewModelLocator
{
public ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
SimpleIoc.Default.Register<MainViewModel>();
SimpleIoc.Default.Register<FirstControlViewModel>();
SimpleIoc.Default.Register<SecondControlViewModel>();
}
public MainViewModel Main
{
get
{
return ServiceLocator.Current.GetInstance<MainViewModel>();
}
}
public FirstControlViewModel FirstControl
{
get { return ServiceLocator.Current.GetInstance<FirstControlViewModel>(); }
}
public SecondControlViewModel SecondControl
{
get { return ServiceLocator.Current.GetInstance<SecondControlViewModel>(); }
}
public static void Cleanup()
{
// TODO Clear the ViewModels
}
}
And I don't know how change CurrentViewModel in MainViewModel from for example FirstControlViewModel's command. Any idea? I thought about some event but this looks not good. Does anyone have any idea?
Thanks
First things first... to change the view, we change the view model (assuming that you have correctly declared a DataTemplate for it):
<ContentControl Content="{Binding CurrentViewModel}"/>
...
CurrentViewModel = new OtherViewModel();
Clearly, you can only do that from your MainViewModel. Therefore, you should handle the Button ICommand in your MainViewModel, so move your SwitchToFirstControlCommand there and change your Button.Command Binding Path to this:
<Button Content="Switch to first control" Command="{Binding DataContext.
SwitchToFirstControlCommand, RelativeSource={RelativeSource
AncestorType={x:Type MainWindow}}}" />
Now in your main view model:
private void SwitchToSecondControlExecute()
{
CurrentViewModel = new OtherViewModel();
}

Categories