TabControl - Share Main ViewModel - c#

I want to be able to share controls between my main window and tab controls. Right now, I have a tab control and status bar on the main window. Status bar contains a button and progress bar. I want to share the progress bar. When the button is clicked, it resets progress bar.
The tabs contain a button. When clicked, it should increase the progress bar by 1.
What I've done: after digging through SO/other all day, I was able to set this up.
MainWindowView
-> Declares instance of MainWindowViewModel in xaml
-> Declares tabs as usercontrols in xaml b/c will always have the same tabs
TabControl1View
-> Declares instance of TabControl2ViewModel in xaml
I need advice on how to talk to MainWindowViewModel in TabControl1View.
<Window x:Class="TabControlTest.MainWindowView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TabControlTest"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<DockPanel>
<StatusBar DockPanel.Dock="Bottom">
<StatusBarItem HorizontalContentAlignment="Stretch" DockPanel.Dock="Top">
<Button Height="25" Content="Reset" Command="{Binding MainWindowViewModel_ButtonClickCommand}"/>
</StatusBarItem>
<StatusBarItem HorizontalContentAlignment="Stretch" DockPanel.Dock="Bottom">
<ProgressBar Height="25" Maximum="10" Value="{Binding MainWindowViewModel_Progress, Mode=TwoWay}"/>
</StatusBarItem>
</StatusBar>
<TabControl>
<TabItem>
<local:TabControl1View/>
</TabItem>
</TabControl>
</DockPanel>
class MainWindowViewModel : ViewModelBase
{
int progress = 0;
public int MainWindowViewModel_Progress
{
get
{
return progress;
}
set
{
SetAndNotify(ref this.progress, value, () => this.MainWindowViewModel_Progress);
}
}
ICommand _ButtonClickCommand;
public ICommand MainWindowViewModel_ButtonClickCommand
{
get
{
return _ButtonClickCommand ?? (_ButtonClickCommand = new CommandHandler(() => MainWindowViewModel_ButtonClick(), true));
}
}
public void MainWindowViewModel_ButtonClick()
{
MainWindowViewModel_Progress = 0; // Reset progress
}
}
x
<UserControl x:Class="TabControlTest.TabControl1View"
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:TabControlTest"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.DataContext>
<local:TabControl1ViewModel/>
</UserControl.DataContext>
<Grid>
<Button Content="UP" Command="{Binding TabControl1ViewModel_UpClickCommand}"/>
</Grid>
class TabControl1ViewModel : ViewModelBase
{
ICommand _UpClickCommand;
public ICommand TabControl1ViewModel_UpClickCommand
{
get
{
return _UpClickCommand ?? (_UpClickCommand = new CommandHandler(() => TabControl1ViewModel_UpClick(), true));
}
}
public void TabControl1ViewModel_UpClick()
{
// I want to increase the progress bar here
}
}
I found a good answer on using a mediator pattern here:
How can I update a property of mainWindowViewModel from another ViewModel?
But I don't understand how I can store a reference to TabControl1ViewModel in MainWindowViewModel.
Also found a ton of articles saying to declare the ViewModels inside MainViewModel and keep track of them, but they were always code snippets.
Question: how do I pass MainViewModel.Progress to TabControlViewModel?
I'm now going down the path of trying to put something like this in my MainViewModel
MainViewModel.ProgressChanged = TabControlView.(get TabControlViewModel).ProgressChanged

When you do this
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
or
<UserControl.DataContext>
<local:TabControl1ViewModel/>
</UserControl.DataContext>
You are letting the View to create a instance of MainWindowViewModel or TabControl1ViewModel by itself and bind it to your View's datacontext. That makes you loose control of passing any constructor parameter to the control.
In your MainWindowViewModel's constructor initialize the User control's Datacontext like
class MainWindowViewModel
{
MainWindowViewModel
{
ChildViewModel = new TabControl1ViewModel(this);
}
public TabControl1ViewModel ChildViewModel {get; private set;}
}
class TabControl1ViewModel
{
public MainWindowViewModel ParentViewModel {get; private set;}
TabControl1ViewModel(MainWindowViewModel mainWindowViewModel)
{
ParentViewModel = mainWindowViewModel;
}
}
View.Xaml
<Window x:Class="TabControlTest.MainWindowView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TabControlTest"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<DockPanel>
.
.
.
<TabControl>
<TabItem>
<local:TabControl1View DataContext={Binding ChildViewModel}/>
</TabItem>
</TabControl>
</DockPanel>

Related

How to set up a dynamically-populated TabControl with ReactiveUI?

I'm trying to wrap my head around ReactiveUI. Most of it makes some degree of sense if you don't look at it too closely, but when I try to set up a TabControl everything explodes in my face.
I have a TabControl on my Window. I want to be able to add multiple different types of tabs to it dynamically at runtime based on different user actions. This answer explains a standard WPF way to do that, and it almost works, but since it's not ReactiveUI, anytime I try to open a tab with a Reactive view on it, everything blows up because the ViewModel dependency property hasn't been bound.
XAML:
<Window.Resources>
<DataTemplate DataType="{x:Type vm:MyTabViewModel}">
<views:MyTabEditor/>
</DataTemplate>
</Window.Resources>
...
<TabControl Name="Multitab" Grid.Column="2" ItemsSource="{Binding Tabs}">
<TabControl.ItemContainerStyle>
<Style TargetType="{x:Type TabItem}">
<Setter Property="Header" Value="{Binding Name}" />
</Style>
</TabControl.ItemContainerStyle>
</TabControl>
ViewModel:
public ObservableCollection<ITabPage> Tabs { get; } = new();
public void AddNewTab()
{
var vm = new MyTabEditorViewModel();
Tabs.Add(vm);
}
XAML.cs
private void NewTab_Executed(object sender, ExecutedRoutedEventArgs e)
{
ViewModel.AddNewJob();
Multitab.SelectedIndex = Multitab.Items.Count - 1;
var last = Multitab.SelectedIndex;
this.OneWayBind(ViewModel, vm => vm.Tabs[last], v => GAH WHAT GOES HERE?!?);
}
And this is the part where I get lost. How do I set up the bindings to bind the new VM to the view that gets created for it? Multitab.SelectedItem returns the VM, not the View, and I can't seem to find any way to obtain the newly-created View object in order to bind it.
Does anyone know how to set this up properly?
Please refer to the below sample code.
Window1.xaml:
<reactiveui:ReactiveWindow x:Class="WpfApp1.Window1"
x:TypeArguments="local:ViewModel"
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:WpfApp1"
xmlns:reactiveui="http://reactiveui.net"
mc:Ignorable="d"
Title="Window1" Height="450" Width="800">
<Grid>
<TabControl Name="Multitab">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<reactiveui:ViewModelViewHost ViewModel="{Binding}" />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
</reactiveui:ReactiveWindow>
Window.xaml.cs:
public partial class Window1 : ReactiveWindow<ViewModel>
{
public Window1()
{
InitializeComponent();
ViewModel = new ViewModel();
this.WhenActivated(disposableRegistration =>
{
this.OneWayBind(ViewModel,
viewModel => viewModel.Tabs,
view => view.Multitab.ItemsSource)
.DisposeWith(disposableRegistration);
});
}
}
View Model:
public class ViewModel
{
public ObservableCollection<ITabPage> Tabs { get; } =
new ObservableCollection<ITabPage>() { new MyTabEditorViewModel() };
}
Tab View Model:
public interface ITabPage { }
public class MyTabEditorViewModel : ITabPage
{
public string Name { get; } = "Name...";
}
TabView.xaml:
<reactiveui:ReactiveUserControl x:Class="WpfApp1.TabEditorView"
x:TypeArguments="local:MyTabEditorViewModel"
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:reactiveui="http://reactiveui.net"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<TextBlock>Tab content...</TextBlock>
</Grid>
</reactiveui:ReactiveUserControl>
TabView.xaml.cs:
public partial class TabEditorView : ReactiveUserControl<MyTabEditorViewModel>
{
public TabEditorView()
{
InitializeComponent();
}
}

Why WPF RaisePropertyChanged can't update reference type binding?

I use one window to change the data, while using another window(MainWindow) to show the data.
Unexpectedly, when MainWindowViewModel catches the PropertyChanged event and RaisePropertyChanged to update MainWindow view, nothing happened in the view.
In the debugger, I found the MainWindowViewModel property has changed,and Debug has printed the message, but view not change.
I'm using Mvvmlight.
Sorry for my poor English.
I'd appreciate it if you could help me. XD!
Here is the View code:
<Window x:Class="OneTimetablePlus.Views.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:OneTimetablePlus.Views"
xmlns:tb="http://www.hardcodet.net/taskbar"
mc:Ignorable="d"
Title="MainWindow" Height="730" Width="91.52"
AllowsTransparency="True"
WindowStyle="None"
Background="Transparent"
ShowInTaskbar="False"
Topmost="True"
ResizeMode="NoResize"
DataContext="{Binding Main, Source={StaticResource Locator}}"
>
<ListBox ItemsSource="{Binding TodayDayCourses}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ShowName}" Style="{StaticResource LargeText}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Window>
Here is the ViewModel code:
public class MainViewModel : ViewModelBase
{
public MainViewModel(IDataProvider dataProvider)
{
DataProvider = dataProvider;
dataProvider.PropertyChanged += (sender, e) =>
{
if (e.PropertyName == GetPropertyName(() => dataProvider.TodayDayCourse))
{
Debug.Print("Catch PropertyChanged TodayDayCourse");
RaisePropertyChanged(() => TodayDayCourses);
}
};
}
public List<Course> TodayDayCourses => DataProvider.TodayDayCourse2;
public IDataProvider DataProvider { get; }
}
As Clemens said, we should use => DataProvider.TodayDayCourse2.ToList() instead of => DataProvider.TodayDayCourse2.
Because the latter always returns the same instance, while is not a property changed.

ContentControl not refreshing View when new ViewModel is created

I am trying to make an app using using MVVMLight that has a sidebar with buttons, each button shows a new usercontrol hosted in a ContentControl. When the user clicks the buttons in the sidebar (Show View1 / Show View2) it shows the views correctly.
My problems arise when the user has already shown a View (it could be View1 or View2 ), say View1 and click (Show View1) ** again ** View1 controls remain with the same values and I would like to show the same view with all controls as if it had restarted. (I know that I could reset all controls within the user control (View1) but my application requires having a new instance of the view every time the button is clicked).
Somehow, in the same scenario when the user is in View 1, switch to view 2 and return to view 1, a new instance is created and shown as I would like. I would like to get the same result without the need to switch to view 2 and return.
Here is a minimal reproducible example of the issue:
ViewModelLocator.cs
using CommonServiceLocator;
using GalaSoft.MvvmLight.Ioc;
namespace SidebarApp.ViewModel
{
public class ViewModelLocator
{
/// <summary>
/// Initializes a new instance of the ViewModelLocator class.
/// </summary>
public ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
// Register viewmodels
SimpleIoc.Default.Register<MainViewModel>();
SimpleIoc.Default.Register<View1ViewModel>();
SimpleIoc.Default.Register<View2ViewModel>();
}
public MainViewModel Main { get { return ServiceLocator.Current.GetInstance<MainViewModel>(); } }
public View1ViewModel View1Vm { get { return ServiceLocator.Current.GetInstance<View1ViewModel>(); } }
public View2ViewModel View2Vm { get { return ServiceLocator.Current.GetInstance<View2ViewModel>(); } }
public static void Cleanup()
{
// TODO Clear the ViewModels
}
}
}
MainViewModel.cs
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using System.Windows.Input;
namespace SidebarApp.ViewModel
{
public class MainViewModel : ViewModelBase
{
// Commands
public ICommand ShowView1Command { get; private set; }
public ICommand ShowView2Command { get; private set; }
/// <summary>
/// Property that will cointain the current viewmodel to show
/// ViewModelBase inplements ObservableObject class which imlements INotifyPropertyChanged
/// </summary>
public ViewModelBase CurrentViewModel { get; set; }
/// <summary>
/// Initializes a new instance of the MainViewModel class.
/// </summary>
public MainViewModel()
{
this.ShowView1Command = new RelayCommand(ShowView1);
this.ShowView2Command = new RelayCommand(ShowView2);
}
private void ShowView1()
{
// I tried this but it doesn't work
//CurrentViewModel = null or View2ViewModel;
CurrentViewModel = new View1ViewModel();
}
private void ShowView2()
{
CurrentViewModel = new View2ViewModel();
}
}
}
MainWindow.xaml
<Window x:Class="SidebarApp.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:SidebarApp"
mc:Ignorable="d"
DataContext="{Binding Main, Source={StaticResource Locator}}"
Title="MainWindow" Height="348.965" Width="560.683">
<Window.Resources>
<DataTemplate DataType="{x:Type local:View1ViewModel}">
<local:View1View />
</DataTemplate>
<DataTemplate DataType="{x:Type local:View2ViewModel}">
<local:View2View />
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.3*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel>
<Button Command="{Binding ShowView1Command}">Show View1</Button>
<Button Command="{Binding ShowView2Command}">Show View2</Button>
</StackPanel>
<ContentControl Grid.Column="1" Content="{Binding Path=CurrentViewModel}"></ContentControl>
</Grid>
</Window>
View1View.xaml
<UserControl x:Class="SidebarApp.View1View"
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:SidebarApp"
mc:Ignorable="d"
DataContext="{Binding View1Vm, Source={StaticResource Locator}}"
d:DesignHeight="450" d:DesignWidth="800">
<Grid Background="LightPink">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock HorizontalAlignment="Center" Text="Welcome to View 1" FontSize="20"/>
<TextBox HorizontalAlignment="Stretch" Text="Original text value"/>
<CheckBox Content="Some boolean value"/>
</StackPanel>
</Grid>
</UserControl>
View1ViewModel.cs
using GalaSoft.MvvmLight;
namespace SidebarApp
{
public class View1ViewModel : ViewModelBase
{
public View1ViewModel()
{
}
}
}
View2View.xaml
<UserControl x:Class="SidebarApp.View2View"
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:SidebarApp"
mc:Ignorable="d"
DataContext="{Binding View2Vm, Source={StaticResource Locator}}"
d:DesignHeight="450" d:DesignWidth="800">
<Grid Background="LightCyan">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock HorizontalAlignment="Center" Text="Welcome to View 2" FontSize="20"/>
<TextBox HorizontalAlignment="Stretch" Text="Some text in view2"/>
<Button Content="Button"/>
</StackPanel>
</Grid>
</UserControl>
View2ViewModel.cs
using GalaSoft.MvvmLight;
namespace SidebarApp
{
public class View2ViewModel : ViewModelBase
{
public View2ViewModel()
{
}
}
}
App.xaml
<Application x:Class="SidebarApp.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:SidebarApp" StartupUri="MainWindow.xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" d1p1:Ignorable="d" xmlns:d1p1="http://schemas.openxmlformats.org/markup-compatibility/2006">
<Application.Resources>
<ResourceDictionary>
<vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True" xmlns:vm="clr-namespace:SidebarApp.ViewModel" />
</ResourceDictionary>
</Application.Resources>
</Application>
For example I enter to the view1 and change the value of the textbox ("Original text value") to another value and I click again in Show View1 the text and everything in the usercontrol stays the same but when I switch to view2 and go back to view1 a new usercontrol is shown with his original values.
This might feel like something of a workaround, but it works. Hopefully it will prove useful. (Obviously make same changes to View2View view/view model).
ViewModelLocator.cs
public View1ViewModel View1Vm { get { return new View1ViewModel(); } }
MainViewViewModel.cs
private void ShowView1()
{
CurrentViewModel = new ViewModelLocator().View1Vm;
}
View1View.xaml
<UserControl x:Class="SidebarApp.View1View"
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:SidebarApp"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid Background="LightPink">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock HorizontalAlignment="Center" Text="Welcome to View 1" FontSize="20"/>
<TextBox HorizontalAlignment="Stretch" Text="{Binding Path=View1Text, Mode=TwoWay}"/>
<CheckBox Content="Some boolean value" IsChecked="{Binding Path=CheckBoxChecked, Mode=TwoWay}"/>
</StackPanel>
</Grid>
</UserControl>
View1ViewModel.cs
public class View1ViewModel : ViewModelBase
{
private string _view1Text;
private bool _checkBoxedChecked;
public string View1Text
{
get
{
return _view1Text;
}
set
{
_view1Text = value;
RaisePropertyChanged("View1Text");
}
}
public bool CheckBoxChecked
{
get
{
return _checkBoxedChecked;
}
set
{
_checkBoxedChecked = value;
RaisePropertyChanged("CheckBoxChecked");
}
}
public View1ViewModel()
{
View1Text = "Original text value";
}
}

How can I reuse a WPF user control but change a bound property?

I have a WPF user control which uses a property from my viewmodel for binding. I now want to use two instances of that user control with the same viewmodel, but override the binding in one of them.
The code looks like this
User control
<UserControl x:Class="TestWpfApp.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestWpfApp">
<Grid>
<DockPanel>
<Label Margin="10" Content="{Binding MyLabel}"/>
<Button Margin="10" Content="{Binding MyButton}"/>
</DockPanel>
</Grid>
</UserControl>
Main view
<Window x:Class="TestWpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestWpfApp"
Title="MainWindow" Height="150" Width="525">
<Window.DataContext>
<local:ViewModel/>
</Window.DataContext>
<Grid>
<DockPanel>
<local:UserControl1 DockPanel.Dock="Top" x:Name="UserControl1"/>
<!--In this instance of UserControl1, I want the Label to bind to the MyNewLabel-->
<local:UserControl1 DockPanel.Dock="Top" x:Name="UserControl2"/>
</DockPanel>
</Grid>
</Window>
Viewmodel
public class ViewModel
{
public string MyLabel => "Label1";
public string MyButton => "Button1";
public string MyNewLabel => "Label2";
}
In the second instance of UserControl1, I would like the label to be bound to a different property. I tried setting that property as a resource on the user control, and then overriding it in the main view but I wasn't able to get that to work.
In reality, I'm using DevExpress controls and POCO viewmodels, if that makes things easier.
When creating custom control - it's not a good idea to bind controls inside that custom control to arbitrary properties provided by external data context. Not only you are getting problems like you have now, user of that control has no idea which datacontext with which properties should he provide for that control to work. Only looking at source code of your UserControl one might find that it expects data context with properties MyLabel and MyButton. Instead - make control self-contained by introducing dependency properties on control itself:
public partial class UserControl1 : UserControl {
public UserControl1() {
InitializeComponent();
}
public string Text
{
get { return (string) GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(UserControl1), new PropertyMetadata(null));
public string ButtonText
{
get { return (string) GetValue(ButtonTextProperty); }
set { SetValue(ButtonTextProperty, value); }
}
public static readonly DependencyProperty ButtonTextProperty =
DependencyProperty.Register("ButtonText", typeof(string), typeof(UserControl1), new PropertyMetadata(null));
}
And bind to those properties:
<UserControl x:Class="WpfApplication1.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="self">
<Grid>
<DockPanel>
<Label Margin="10"
Content="{Binding Text, ElementName=self}" />
<Button Margin="10"
Content="{Binding ButtonText, ElementName=self}" />
</DockPanel>
</Grid>
</UserControl>
Then in main window - bind those dependency properties to your model:
<Window.DataContext>
<my:ViewModel />
</Window.DataContext>
<Grid>
<DockPanel>
<my:UserControl1 DockPanel.Dock="Top"
x:Name="UserControl1" Text="{Binding MyLabel}" ButtonText="{Binding MyButton}" />
<!--In this instance of UserControl1, I want the Label to bind to the MyNewLabel-->
<my:UserControl1 DockPanel.Dock="Top"
x:Name="UserControl2"
Text="{Binding MyNewLabel}"
ButtonText="{Binding MyButton}" />
</DockPanel>
</Grid>

Add a usercontrol to caliburm micro dynamically

I am using Caliburn Micro with WPF. I want to create an application with a menu on the left side, and a grid on the right side of the application. When clicking on a menu item, the grid on the right side, will change to another view. The another view will be in a separate file.
MainWindowView:
<UserControl x:Class="CMDemo.Views.MainWindowView"
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>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="90*" />
<ColumnDefinition Width="210*" />
</Grid.ColumnDefinitions>
<StackPanel Name="LeftMenu">
<Button Name="ChangeDisplay" Content="Click Me"></Button>
<TextBlock x:Name="MyString"></TextBlock>
</StackPanel>
<Grid Grid.Column="1" x:Name="MainGridContent" />
</Grid>
MainWindowViewModel:
public class MainWindowViewModel : PropertyChangedBase
{
private UserControl mainGridContent;
private string myString;
public UserControl MainGridContent
{
get { return this.mainGridContent; }
set
{
this.mainGridContent = value;
NotifyOfPropertyChange(() => this.MainGridContent);
}
}
public string MyString
{
get { return this.myString; }
set
{
this.myString = value;
NotifyOfPropertyChange(() => this.MyString);
}
}
public void ChangeDisplay()
{
this.MainGridContent = new ChangeDisplayView();
this.MyString = "Testing....";
}
}
The changeDisplayViewModel:
public class changeDisplayViewModel: PropertyChangedBase
{
}
The changeDisplayView:
<UserControl x:Class="CMDemo.Views.changeDisplayView"
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>
<TextBox content="Hello Caliburn Micro">
</Grid>
When I click the "Click Me" button the TextBlock "MyString" is updated and showing, but the usercontrol is not. What am I doing wrong?
Try changing MainGridContent to a changeDisplayViewModel rather than the view itself.

Categories

Resources