Passing Data to a UserControl - c#

I'm trying to pass an ObservableCollection<object> to a UserControl through XAML bindings. My MainWindow class generates a random list of Student. The list is correctly generated then the components are initialized. In my XAML File I bind the students but the UserControl class does not seem to have any data.
MainWindow.xaml.cs
public partial class MainWindow : Window
{
private ObservableCollection<object> _Students;
public ObservableCollection<object> Students { get => GetStudents(); }
public MainWindow()
{
GenerateStudentList();
Console.WriteLine("MainWindow: Students = {0}", Students.Count);
InitializeComponent();
}
private void GenerateStudentList()
{
for (int i = 0; i < 20; i++)
{
var s = Student.GenRandomStudent();
Console.WriteLine("Student: {0} - {1} {2}, {3}", s.StudentId, s.FirstName, s.LastName, s.Age);
Students.Add(s);
}
}
private ObservableCollection<object> GetStudents()
{
if (_Students == null)
_Students = new ObservableCollection<object>();
return _Students;
}
}
MainWindow.xaml
<Window x:Class="StudentApp.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:localCtr="clr-namespace:StudentApp.Controls"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<DockPanel>
<Menu VerticalAlignment="Top" DockPanel.Dock="Top">
<MenuItem Header="_File">
<MenuItem Header="_Abot"/>
<Separator/>
<MenuItem Header="_Exit"/>
</MenuItem>
</Menu>
<StatusBar DockPanel.Dock="Bottom" Height="auto">
<StatusBarItem Content="Status Bar"/>
</StatusBar>
<Grid DockPanel.Dock="Left" MinWidth="250">
<localCtr:StudentListBox Students="{Binding Students}" ContentStringFormat="{}{0} Students"/>
</Grid>
<Grid DockPanel.Dock="Right">
<Label Content="{Binding Students.Count}"/>
</Grid>
</DockPanel>
</Grid>
</Window>
StudentListBox.xaml.cs
public partial class StudentListBox : UserControl
{
public ObservableCollection<Object> Students
{
get { return (ObservableCollection<Object>)GetValue(StudentsProperty) ; }
set { SetValue(StudentsProperty, value); }
}
// Generated with 'propdp'
public static readonly DependencyProperty StudentsProperty =
DependencyProperty.Register(
"Students",
typeof(ObservableCollection<Object>),
typeof(StudentListBox),
new PropertyMetadata(new ObservableCollection<Object>())
);
///
public StudentListBox()
{
Console.WriteLine("StudentListBox: Students = {0}", Students.Count);
InitializeComponent();
}
}
StudentListBox.xaml
<UserControl x:Class="StudentApp.Controls.StudentListBox"
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:StudentApp.Controls"
mc:Ignorable="d"
Name="StudentListBoxControl">
<Grid>
<Label Content="{Binding Students.Count, ElementName=StudentListBoxControl}"
ContentStringFormat="{}{0} Students"/>
</Grid>
</UserControl>

I Fixed my issue! I forgot to use DataContext:
public MainWindow()
{
GenerateStudentList();
this.DataContext = this; // <---- Right here!
InitializeComponent();
}

Related

Page doesn't retain UI changes in multi-page WPF/MVVM application

I have a simple multi-page MVVM Light application, my issue is that if I draw something on the screen or change the background color of a button in one of the views/pages then go to a different page and come back, the drawn content is no longer there.
MAIN PAGE CODE:
XAML
<Window x:Class="TwoViews.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"
Title="MVVM Light View Switching"
d:DesignHeight="300"
d:DesignWidth="300"
DataContext="{Binding Main,
Source={StaticResource Locator}}"
ResizeMode="NoResize"
SizeToContent="WidthAndHeight"
mc:Ignorable="d">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ContentControl Content="{Binding CurrentViewModel}" />
<DockPanel Grid.Row="1" Margin="5">
<Button Width="75"
Height="23"
Command="{Binding SecondViewCommand}"
Content="Second View"
DockPanel.Dock="Right" />
<Button Width="75"
Height="23"
Command="{Binding FirstViewCommand}"
Content="First View"
DockPanel.Dock="Left" />
</DockPanel>
</Grid>
</Window>
ViewModel
using System.Windows.Input;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
namespace TwoViews.ViewModels
{
public class MainViewModel : ViewModelBase
{
private ViewModelBase _currentViewModel;
readonly static SecondViewModel _secondViewModel = new SecondViewModel();
readonly static FirstViewModel _firstViewModel = new FirstViewModel();
public ViewModelBase CurrentViewModel
{
get
{
return _currentViewModel;
}
set
{
if (_currentViewModel == value)
return;
_currentViewModel = value;
RaisePropertyChanged("CurrentViewModel");
}
}
public ICommand FirstViewCommand { get; private set; }
public ICommand SecondViewCommand { get; private set; }
public MainViewModel()
{
CurrentViewModel = MainViewModel._firstViewModel;
FirstViewCommand = new RelayCommand(() => ExecuteFirstViewCommand());
SecondViewCommand = new RelayCommand(() => ExecuteSecondViewCommand());
}
private void ExecuteFirstViewCommand()
{
CurrentViewModel = MainViewModel._firstViewModel;
}
private void ExecuteSecondViewCommand()
{
CurrentViewModel = MainViewModel._secondViewModel;
}
}
}
Codebehind
namespace TwoViews
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}
FIRST PAGE CODE:
XAML
<UserControl x:Class="TwoViews.Views.FirstView"
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"
d:DesignHeight="300"
d:DesignWidth="300"
mc:Ignorable="d">
<Grid>
<Label x:Name="label" Content="First Page" HorizontalAlignment="Left" Margin="92,46,0,0" VerticalAlignment="Top"/>
</Grid>
</UserControl>
ViewModel
namespace TwoViews.ViewModels
{
public class FirstViewModel : ViewModelBase
{
public FirstViewModel()
{
}
}
}
Codebehind
namespace TwoViews.Views
{
public partial class FirstView : UserControl
{
public FirstView()
{
InitializeComponent();
}
}
}
SECOND PAGE CODE
XAML
<UserControl x:Class="TwoViews.Views.SecondView"
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"
d:DesignHeight="300"
d:DesignWidth="300"
mc:Ignorable="d">
<Grid Name="myGrid">
<Grid Name="GridSecondPage" HorizontalAlignment="Left" Height="107" Margin="10,43,0,0" VerticalAlignment="Top" Width="280"/>
<Button x:Name="DrawRectangle" Content="Draw" HorizontalAlignment="Left" Margin="135,203,0,0" VerticalAlignment="Top" Width="75" Click="DrawRectangle_Click"/>
</Grid>
</UserControl>
ViewModel
namespace TwoViews.ViewModels
{
public class SecondViewModel : ViewModelBase
{
}
}
Codebehind
namespace TwoViews.Views
{
public partial class SecondView : UserControl
{
public SecondView()
{
InitializeComponent();
}
private void DrawRectangle_Click(object sender, RoutedEventArgs e)
{
Rectangle myRectangle = new Rectangle();
myRectangle.Name = "myRectangle";
myRectangle.Width = 100;
myRectangle.Height = 100;
myRectangle.Fill = Brushes.Blue;
GridSecondPage.Children.Add(myRectangle);
}
}
}

Custom Tooltip on LineChart (lvChart)

I closely follow step-by-step lvChart Customizing Tooltips in trying to build a custom tooltip for LineChart points. But I get empty tooltip content. Other than using ObservableValue for ChartValues type, my code is near identical to that used in the lvChart tutorial. I am not using any MV**.
Has anyone worked out the tutorial or apply custom tooltip on a LineChart?
Main.xaml
<lvc:CartesianChart.DataTooltip>
<local:MyTooltip />
</lvc:CartesianChart.DataTooltip>
Main.xaml.cs
MyTooltipContents = new ChartValues<MyTooltipContent>();
for (int i = 0; i < MyData.Count(); i++)
{
MyTooltipContents.Add(new MyTooltipContent
{
Name = "No" + i,
Value = "Foo"
});
}
var MyTooltipContentMapper = Mappers.Xy<MyTooltipContent>()
.X((value, index) => index)
.Y(value => 1);
//lets save the mapper globally
Charting.For<MyTooltipContent>(MyTooltipContentMapper);
DataContext = this;
MyTooltip.xaml
<UserControl x:Class="MyNamespace.MyTooltip"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MyNamespace"
xmlns:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
d:DataContext="{d:DesignInstance local:MyTooltip}"
Background="#E4555555" Padding="20 10" BorderThickness="2" BorderBrush="#555555">
<ItemsControl ItemsSource="{Binding Data.Points}" Grid.IsSharedSizeScope="True">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type lvc:DataPointViewModel}">
<Grid Margin="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="Name"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="Value"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="1" Text="{Binding ChartPoint.Instance.(local:MyTooltipContent.Name)}"
Margin="5 0 0 0" VerticalAlignment="Center" Foreground="White" />
<TextBlock Grid.Column="2" Text="{Binding ChartPoint.Instance.(local:MyTooltipContent.Value)}"
Margin="5 0 0 0" VerticalAlignment="Center" Foreground="White"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</UserControl>
MyToolTip.xaml.cs
public partial class MyTooltip : IChartTooltip
{
private TooltipData _data;
public MyTooltip()
{
InitializeComponent();
DataContext = this;
}
public event PropertyChangedEventHandler PropertyChanged;
public TooltipData Data
{
get { return _data; }
set
{
_data = value;
OnPropertyChanged("Data");
}
}
public TooltipSelectionMode? SelectionMode { get; set; }
protected virtual void OnPropertyChanged(string propertyName = null)
{
if (PropertyChanged != null)
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
MyTooltipContent.cs
public class MyTooltipContent
{
public string Name { get; set; }
public string Value { get; set; }
}
I copied your example, and it works for me.
You might want to look at your MyTooltipContents declaration on your Main.xaml.cs, which should be a public property:
public ChartValues<MyTooltipContent> MyTooltipContents { get; set; }
I don't know exactly what gave you your line series, but I used this one on my MainWindow.xaml (corresponds to your Main.xaml):
<Window x:Class="MyToolTipExample.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:MyToolTipExample"
xmlns:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<lvc:CartesianChart LegendLocation="Right">
<lvc:CartesianChart.Series>
<lvc:LineSeries Title="Customers" Values="{Binding MyTooltipContents}"></lvc:LineSeries>
</lvc:CartesianChart.Series>
<lvc:CartesianChart.DataTooltip>
<local:MyTooltip/>
</lvc:CartesianChart.DataTooltip>
</lvc:CartesianChart>
</Grid>
</Window>
Note that MyTooltipContents is bound to the LineSeries.
EDIT: included screenshot.
Below, the full code example (.Net Framework 4.8 and LiveCharts 0.9.7):
1 - MainWindow.xaml.cs (the corresponding MainWindow.xaml is above):
namespace MyToolTipExample
{
public partial class MainWindow : Window
{
public ChartValues<MyTooltipContent> MyTooltipContents { get; set; }
public MainWindow()
{
InitializeComponent();
MyTooltipContents = new ChartValues<MyTooltipContent>();
for (int i = 0; i < 50; i++)
{
MyTooltipContents.Add(new MyTooltipContent
{
Name = $"No{i}",
Value = "Foo"
});
}
var MyTooltipContentMapper = Mappers.Xy<MyTooltipContent>()
.X((value, index) => index)
.Y(value => 1);
//lets save the mapper globally
Charting.For<MyTooltipContent>(MyTooltipContentMapper);
DataContext = this;
}
}
}
2 - the UserControl MyTooltip.xaml:
<UserControl x:Class="MyToolTipExample.MyTooltip"
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:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf"
xmlns:local="clr-namespace:MyToolTipExample"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance local:MyTooltip}"
Background="#45555555" Padding="20 10" BorderThickness="2" BorderBrush="#555555">
<ItemsControl ItemsSource="{Binding Data.Points}" Grid.IsSharedSizeScope="True">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type lvc:DataPointViewModel}">
<Grid Margin="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="Name"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="Value"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="1" Text="{Binding ChartPoint.Instance.(local:MyTooltipContent.Name)}"
Margin="5 0 0 0" VerticalAlignment="Center" Foreground="White" />
<TextBlock Grid.Column="2" Text="{Binding ChartPoint.Instance.(local:MyTooltipContent.Value)}"
Margin="5 0 0 0" VerticalAlignment="Center" Foreground="White"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</UserControl>
3 - Code-behind the UserControl MyTooltip.xaml.cs:
using LiveCharts;
using LiveCharts.Wpf;
using System.ComponentModel;
using System.Windows.Controls;
namespace MyToolTipExample
{
public partial class MyTooltip : UserControl, IChartTooltip
{
public MyTooltip()
{
InitializeComponent();
DataContext = this;
}
private TooltipData _data;
public event PropertyChangedEventHandler PropertyChanged;
public TooltipData Data
{
get { return _data; }
set
{
_data = value;
OnPropertyChanged("Data");
}
}
public TooltipSelectionMode? SelectionMode { get; set; }
protected virtual void OnPropertyChanged(string propertyName = null)
{
if (PropertyChanged != null)
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
4 - Data model MyTooltipContent.cs (used by public property ChartValues):
namespace MyToolTipExample
{
public class MyTooltipContent
{
public string Name { get; set; }
public string Value { get; set; }
}
}
5 - Package.config:
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="LiveCharts" version="0.9.7" targetFramework="net48" />
<package id="LiveCharts.Wpf" version="0.9.7" targetFramework="net48" />
</packages>
6 - The final solution structure should look something like this:
The application would be crashed when I used the PieChart to customize the ToolTip?
<Window x:Class="MyToolTipExample.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:MyToolTipExample"
xmlns:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>`enter code here`
<lvc:PieChart>
<lvc:PieChart.Series>
<lvc:PieSeries Fill="Green" Stroke="{x:Null}" StrokeThickness="0" />
</lvc:PieChart.Series>
<lvc:PieChart.DataTooltip>
<local:MyTooltip/>
</lvc:PieChart.DataTooltip>
</lvc:PieChart>
</Grid>
</Window>

Wpf binding collection property in UserControl (xaml)

Add collections of buttons in my userControl (Options).
In xaml disigner the are displayed.
Output
When i run my application:
If Options not initialized, then an error XamlObjectWriterException: Property collection "WpfAppUserControl.Buttons"."Options" (null).
If Options = new List(), then window without buttons
MainWindow.xaml
<Window x:Class="WpfAppUserControl.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:WpfAppUserControl"
mc:Ignorable="d"
Title="MainWindow" Height="250" Width="300">
<Grid>
<local:Buttons x:Name="Buttons"
VerticalAlignment="Center"
HorizontalAlignment="Center"
HorizontalContentAlignment="Center">
<local:Buttons.Options>
<Button Content="Agudabi 1" Height="20" Margin="2" />
<Button Content="Agudabi 2" Height="20" Margin="2" />
<Button Content="Agudabi 3" Height="20" Margin="2" />
</local:Buttons.Options>
</local:Buttons>
</Grid>
</Window>
Buttons.xaml
<UserControl x:Class="WpfAppUserControl.Buttons"
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:WpfAppUserControl"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<WrapPanel x:Name="InternalContainer" Orientation="Horizontal" HorizontalAlignment="Center"/>
</Grid>
</UserControl>
Buttons.xaml.cs
#region Usings
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
#endregion
namespace WpfAppUserControl
{
public partial class Buttons : UserControl
{
public Buttons()
{
//Options = new ObservableCollection<Button>();
InitializeComponent();
}
public ObservableCollection<Button> Options
{
get { return (ObservableCollection<Button>) GetValue(OptionsProperty); }
set { SetValue(OptionsProperty, value); }
}
public static readonly DependencyProperty OptionsProperty =
DependencyProperty.Register(nameof(Options), typeof(ObservableCollection<Button>), typeof(Buttons),
new PropertyMetadata(/*new ObservableCollection<Button>()*/, PropertyChangedCallback));
private static void PropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var obj = d as Buttons;
foreach (var button in obj.Options)
{
obj.InternalContainer.Children.Add(button);
}
}
}
}
Here is an example of what you should actually do.
Instead of a UserControl with a collection property, use an ItemsControl like this:
<ItemsControl ItemsSource="{Binding Options}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Name}" Command="{Binding Command}"
Height="20" Margin="2"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Then create a view model with a collection of data items with properties for the Button Content and Command:
public class ViewModel
{
public ObservableCollection<Option> Options { get; }
= new ObservableCollection<Option>();
}
public class Option
{
public string Name { get; set; }
public ICommand Command { get; set; }
}
Initialize it like shown below, where the ICommand implementation is ommited for brevity. Search the web for RelayCommand for a the implementation details.
public MainWindow()
{
InitializeComponent();
var vm = new ViewModel();
vm.Options.Add(new Option { Name = "Agudabi 1" });
vm.Options.Add(new Option { Name = "Agudabi 2" });
vm.Options.Add(new Option { Name = "Agudabi 3" });
DataContext = vm;
}
first initialize OptionsProperty with an empty ObservableCollection:
public partial class Buttons : UserControl
{
public Buttons()
{
Options = new ObservableCollection<Button>();
InitializeComponent();
}
public ObservableCollection<Button> Options
{
get { return (ObservableCollection<Button>) GetValue(OptionsProperty); }
set { SetValue(OptionsProperty, value); }
}
public static readonly DependencyProperty OptionsProperty =
DependencyProperty.Register("Options", typeof(ObservableCollection<Button>), typeof(Buttons));
}
Clemens commented that "Never set the default value of a collection type DP to anything else than null. Otherwise all instances of the UserControl class will operate on the same default collection instance." The same is true about any reference type. So property initialization is done in constructor.
you can do without PropertyChangedCallback, because it is possible to display collection more effectively using ItemsControl:
<UserControl x:Name="myUC" x:Class="WpfAppUserControl.Buttons"
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:WpfAppUserControl"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<ItemsControl ItemsSource="{Binding Path=Options, ElementName=myUC}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel x:Name="InternalContainer" Orientation="Horizontal" HorizontalAlignment="Center"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Grid>
</UserControl>
note that PropertyChangedCallback is not triggered when you add items to collections because collection property itself hasn't changed, it is the same reference

New UserControl not showing up

I have UserControl called "LinqView" where I have menu with buttons.
After user click button I want to display new UserControl using MVVM.
I have created new model class called "UsersModel" and new UserControl called "ViewUsersUserControl".
But I don't know why is not working.
Below is my xaml & cs code.
LinqView.xaml
<UserControl x:Class="LayoutMVVM.Views.LinqView"
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:LayoutMVVM.Views"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
xmlns:veiwmodels="clr-namespace:LayoutMVVM.ViewModels.MojeDBModel"
xmlns:views="clr-namespace:LayoutMVVM.Views.MojeDBViews" >
<UserControl.Resources>
<DataTemplate x:Name="UsersTemp" DataType="{x:Type veiwmodels:UsersModel}">
<views:ViewUsersUserControl DataContext="{Binding}" />
</DataTemplate>
</UserControl.Resources>
<Grid Background="LemonChiffon">
<Menu Height="32" Name="Menu" VerticalAlignment="Top">
<MenuItem Header="_Menu">
<MenuItem Header="Add User" Click="MenuItem_VU" />
</MenuItem>
</Menu>
</Grid>
</UserControl>
LinqView.cs
private void MenuItem_VU(object sender, RoutedEventArgs e)
{
DataContext = new UsersModel();
}
Tried like this?
<UserControl x:Class="LayoutMVVM.Views.LinqView"
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:LayoutMVVM.Views"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
xmlns:veiwmodels="clr-namespace:LayoutMVVM.ViewModels.MojeDBModel"
xmlns:views="clr-namespace:LayoutMVVM.Views.MojeDBViews" >
<UserControl.Resources>
<DataTemplate DataType="{x:Type veiwmodels:UsersModel}">
<views:ViewUsersUserControl DataContext="{Binding}" />
</DataTemplate>
</UserControl.Resources>
<Grid Background="LemonChiffon">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Menu " Height="32" Name="Menu" VerticalAlignment="Top">
<MenuItem Header="_Menu">
<MenuItem Header="Add User" Click="MenuItem_VU" />
</MenuItem>
</Menu>
<ContentControl Grid.Row="1" Content="{Binding UsersModel}"/>
</Grid>
</UserControl>
and your C# class should look like,
public class MainViewModel
{
public UsersModel UsersModel {get;set;}
// other properties
}
and in menu click,
private void MenuItem_VU(object sender, RoutedEventArgs e)
{
DataContext = new MainViewModel();
}
i have removed key on DataTemplate.
Update:
Simple working sample,
MainWindow.cs
namespace WpfApplication29
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainViewModel();
}
}
public class MainViewModel : INotifyPropertyChanged
{
public LinqViewModel LinqModel
{
get; set;
} = new LinqViewModel();
public MainViewModel()
{
SelectedMainModel = LinqModel;
}
private object selectedModel;
public object SelectedMainModel
{
get
{
return selectedModel;
}
set
{
selectedModel = value;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void Notify(string name)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
public class LinqViewModel : INotifyPropertyChanged
{
public UserModel UserModel
{
get; set;
} = new UserModel();
private object selectedChildModel;
public object SelectedChildModel
{
get
{
return selectedChildModel;
}
set
{
selectedChildModel = value;
Notify("SelectedChildModel");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void Notify(string name)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
public class UserModel
{
}
}
MainWindow.xaml
<Window
x:Class="WpfApplication29.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:local="clr-namespace:WpfApplication29"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow"
Width="525"
Height="350"
mc:Ignorable="d">
<Window.Resources>
<DataTemplate DataType="{x:Type local:LinqViewModel}">
<local:LinqView />
</DataTemplate>
<DataTemplate DataType="{x:Type local:UserModel}">
<local:UserView />
</DataTemplate>
</Window.Resources>
<Grid>
<ContentControl Content="{Binding SelectedMainModel}" />
</Grid>
</Window>
User UserControl
<UserControl
x:Class="WpfApplication29.UserView"
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:local="clr-namespace:WpfApplication29"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="300"
d:DesignWidth="300"
mc:Ignorable="d">
<Grid >
<TextBlock Text="From UserView"/>
</Grid>
</UserControl>
LinqView Usercontrol xaml
<UserControl
x:Class="WpfApplication29.LinqView"
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:local="clr-namespace:WpfApplication29"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="300"
d:DesignWidth="300"
mc:Ignorable="d">
<Grid Background="LemonChiffon">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Menu Height="32" Name="Menu" VerticalAlignment="Top">
<MenuItem Header="_Menu">
<MenuItem Header="Add User" Click="MenuItem_VU" />
</MenuItem>
</Menu>
<ContentControl Grid.Row="1" Content="{Binding SelectedChildModel}"/>
</Grid>
</UserControl>
Linqview usercontrol cs
namespace WpfApplication29
{
/// <summary>
/// Interaction logic for LinqView.xaml
/// </summary>
public partial class LinqView : UserControl
{
public LinqView()
{
InitializeComponent();
}
private void MenuItem_VU(object sender, RoutedEventArgs e)
{
(this.DataContext as LinqViewModel).SelectedChildModel = (this.DataContext as LinqViewModel).UserModel;
}
}
}
this sample follows multilevel hierarchy. hope this helps.
If you have only one UsersModel you dont need to have it as resource template:
<UserControl x:Class="LayoutMVVM.Views.LinqView"
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:LayoutMVVM.Views"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
xmlns:veiwmodels="clr-namespace:LayoutMVVM.ViewModels.MojeDBModel"
xmlns:views="clr-namespace:LayoutMVVM.Views.MojeDBViews" >
<DockPanel Background="LemonChiffon">
<Menu Height="32" Name="Menu" DockPanel.Dock="Top">
<MenuItem Header="_Menu">
<MenuItem Header="Add User" />
</MenuItem>
</Menu>
<views:ViewUsersUserControl />
</DockPanel>
</UserControl>

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