probleme dealing with several view in mvvm - c#

i'am currently building a software who as several view and i would like to switch from one view to another by clicking on a button on the current view :
of course i have more than 3 views but it's to illustrate the concept. here is my code to go from page 1 to page 2. but i have trouble to go from page 2 to page 3 i don't know what's wrong. thank you for your help.
MainWindow.xaml
<Controls:MetroWindow x:Class="maquette.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:Controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
xmlns:local="clr-namespace:maquette"
xmlns:viewModel ="clr-namespace:maquette.ViewModel"
xmlns:view ="clr-namespace:maquette.View"
BorderBrush="{DynamicResource AccentColorBrush}"
BorderThickness="2"
mc:Ignorable="d"
Title="MainWindow">
<Window.Resources>
<DataTemplate DataType="{x:Type viewModel:page1ViewModel}">
<local:Page1/>
</DataTemplate>
<DataTemplate DataType="{x:Type viewModel:page2ViewModel}">
<view:Page2/>
</DataTemplate>
<DataTemplate DataType="{x:Type viewModel:page3ViewModel}">
<view:Page3/>
</DataTemplate>
<DataTemplate DataType="{x:Type viewModel:page4ViewModel}">
<view:Page4/>
</DataTemplate>
</Window.Resources>
<Grid>
<Controls:TransitioningContentControl x:Name="pagesControl"
Content="{Binding SelectedViewModel}"
Transition="Left"
/>
</Grid>
</Controls:MetroWindow>
MainWindow.cs
public MainWindow()
{
InitializeComponent();
var viewModel = new NavigationViewModel();
viewModel.SelectedViewModel = new page1ViewModel(viewModel);
this.DataContext = viewModel;
}
NavigationViewModel.cs
public class NavigationViewModel : INotifyPropertyChanged
{
private object selectedViewModel;
public object SelectedViewModel
{
get
{
return selectedViewModel;
}
set
{
selectedViewModel = value;
OnPropertyChanged("SelectedViewModel");
}
}
/// <summary>
///
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
Page1.xaml
<UserControl x:Class="maquette.Page1"
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:maquette.View"
xmlns:viewModel="clr-namespace:maquette.ViewModel"
d:DesignHeight="300" d:DesignWidth="300"
mc:Ignorable="d"
>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="100"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock x:Name="Title"
HorizontalAlignment="Center"
TextWrapping="Wrap"
VerticalAlignment="Center"
Width="320"
FontSize="30"
Grid.ColumnSpan="2"
FontFamily="Segoe UI"
FontWeight="Bold">File Control Program</TextBlock>
<Button x:Name="button"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Grid.RowSpan="2"
Width="180"
Height="90"
Style="{StaticResource AccentedSquareButtonStyle}"
Command="{Binding Path=goSettings}"
>
<TextBlock Text="go page 2"
TextWrapping="Wrap"
TextAlignment="Center"/>
</Button>
</Grid>
</UserControl>
page1ViewModel
class page1ViewModel
{
public ICommand goSettings { get; set; }
private readonly NavigationViewModel _navigationViewModel;
public page1ViewModel(NavigationViewModel navigationViewModel)
{
_navigationViewModel = navigationViewModel;
goSettings = new BaseCommand(OpenSettings);
}
/// <summary>
///
/// </summary>
/// <param name="obj"></param>
private void OpenSettings(object obj)
{
_navigationViewModel.SelectedViewModel = new page2ViewModel();
}
}
page2.xaml
<UserControl x:Class="maquette.View.Page2"
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:Controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
xmlns:local="clr-namespace:maquette.View"
xmlns:view ="clr-namespace:maquette.View"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
Background="White">
<Grid>
<Button x:Name="button"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Grid.ColumnSpan="2"
Grid.Row="4"
Width="180"
Height="80"
Style="{StaticResource AccentedSquareButtonStyle}"
Command="{Binding Path=goPage3}">
<TextBlock Text="Go page 3"
TextWrapping="Wrap"
TextAlignment="Center"
FontSize="20"
/>
</Button>
</Grid>
</UserControl>
page2.cs
public Page2()
{
InitializeComponent();
var viewModel = new NavigationViewModel();
DataContext = new page2ViewModel(viewModel);
}
page2ViewModel.cs
public class page2ViewModel
{
public ICommand goPage3 { get; set; }
private readonly NavigationViewModel _navigationViewModel;
public page2ViewModel()
{
}
public page2ViewModel(NavigationViewModel navigationViewModel)
{
_navigationViewModel = navigationViewModel;
goPage3 = new BaseCommand(OpenPage3);
}
/// <summary>
///
/// </summary>
/// <param name="obj"></param>
private void OpenPage3(object obj)
{
_navigationViewModel.SelectedViewModel = new page3ViewModel();
}
}
But as i said the transition between the page 2 and 3 doesn't work. any help ? thank you !

Remove this constructor overload from page2ViewModel:
public page2ViewModel()
{
}
And always inject it with the one and only NavigationViewModel in page1ViewModel:
private void OpenSettings(object obj)
{
_navigationViewModel.SelectedViewModel = new page2ViewModel(_navigationViewModel);
}
Also don't set the DataContext of Page2 explicitly:
public Page2()
{
InitializeComponent();
}
The data templating will make sure that it gets the correct DataContext.

This is for Without using any mvvm framework
Define Data Templates for child viewmodels in Main window viewmodel.
For entire application u have to create static object for your main view model.
Then only views will be changed.
It should be like this...
public partial class App : Application
{
public static MainWindowViewModel mainWindowViewModel;
public App()
{
mainWindowViewModel = new MainWindowViewModel();
}
}
In MainWindow Viewmodel
public class MainWindowViewmodel
{
private object selectedViewModel;
public object SelectedViewModel
{
get
{
return selectedViewModel;
}
set
{
selectedViewModel = value;
OnPropertyChanged("SelectedViewModel");
}
}
public MainWindowViewmodel()
{
SelectedViewModel = new page1viewmodel();
}
}
In Page1Viewmodel in
class page1ViewModel
{
public ICommand goSettings { get; set; }
private readonly NavigationViewModel _navigationViewModel;
public page1ViewModel(NavigationViewModel navigationViewModel)
{
_navigationViewModel = navigationViewModel;
goSettings = new BaseCommand(OpenSettings);
}
/// <summary>
///
/// </summary>
/// <param name="obj"></param>
private void OpenSettings(object obj)
{
mainWindowViewModel.SelectedViewModel = new page2ViewModel();
}
}
like this you have to implement.
This will help you.

Related

How can I pass DataContext to the UserControl using Command in WPF MVVM? [duplicate]

This question already has answers here:
How to pass data from MainWindow to a User Control that's inside the MainWindow?
(1 answer)
Issue with DependencyProperty binding
(3 answers)
Closed 1 year ago.
I am on practice using WPF MVVM with a YouTube video. I made menu buttons in a main window and UserControls, and when I click a menu the UserControl is shown in the main window. The MainViewModel has each ViewModel of the UserControl, and the shown UserControl is changed by changing CurrentView in the MainViewModel.
I want to set the DataContext in the UserControl before it is initialized, but I do not know how. I tried to change ViewModel member values in the MainViewModel, but the actual DataContext member variables in the UserControl are just null.
What I want to ask is how I can set the DataContext in the UserControl when it initialize. Or is there any way to access DataContext from MainWindow?
My Code is below:
Application.xaml
<Application x:Class="Practice.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Practice"
xmlns:viewModel="clr-namespace:Practice.MVVM.ViewModel"
xmlns:view="clr-namespace:Practice.MVVM.View"
Startup="Application_Startup">
<Application.Resources>
<ResourceDictionary>
<DataTemplate DataType="{x:Type viewModel:HomeViewModel}">
<view:HomeView/>
</DataTemplate>
<DataTemplate DataType="{x:Type viewModel:UserViewModel}">
<view:UserView/>
</DataTemplate>
</ResourceDictionary>
</Application.Resources>
</Application>
MainWindow.xaml
<Window x:Class="Practice.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:Practice"
xmlns:viewModel="clr-namespace:Practice.MVVM.ViewModel"
xmlns:view="clr-namespace:Practice.MVVM.View"
mc:Ignorable="d"
Title="Practice" MinHeight="800" MinWidth="1200"
WindowStyle="None"
ResizeMode="NoResize"
Background="Transparent"
AllowsTransparency="True"
Loaded="Window_Loaded">
<Window.DataContext>
<viewModel:MainViewModel/>
</Window.DataContext>
<Border Background="#002241"
CornerRadius="10">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="100"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0"
Text="Logo"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="20"
Foreground="White"/>
<Grid Grid.Row="1" Grid.Column="0">
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
<RowDefinition Height="75"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0">
<RadioButton Content="Home"
Height="50"
Foreground="White"
FontSize="16"
IsChecked="True"
Command="{Binding HomeViewCommand}"/>
<RadioButton Content="Users"
Height="50"
Foreground="White"
FontSize="16"
Command="{Binding UserViewCommand}"/>
</StackPanel>
</Grid>
<ContentControl Grid.Row="1" Grid.Column="1"
x:Name="CC_View"
Margin="10"
Content="{Binding CurrentView}"/>
</Grid>
</Border>
</Window>
HomeView.xaml
<UserControl x:Class="Practice.MVVM.View.HomeView"
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:Practice.MVVM.View"
xmlns:viewModel="clr-namespace:Practice.MVVM.ViewModel"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.DataContext>
<viewModel:HomeViewModel/>
</UserControl.DataContext>
<Grid>
</Grid>
</UserControl>
ObservableObject.cs
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace Practice.Core
{
public class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
}
RelayCommand.cs
using System;
using System.Windows.Input;
namespace Practice.Core
{
public class RelayCommand:ICommand
{
private Action<object> _execute;
private Func<object, bool> _canExecute;
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute == null || _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
}
}
MainViewModel.cs
namespace Practice.MVVM.ViewModel
{
public class MainViewModel : ObservableObject
{
public RelayCommand HomeViewCommand { get; set; }
public RelayCommand UserViewCommand { get; set; }
public HomeViewModel HomeVM { get; set; }
public UserViewModel UserVM { get; set; }
private object _currentView;
private object parameter1;
private object parameter2;
private object parameter3;
public object CurrentView
{
get { return _currentView; }
set
{
_currentView = value;
OnPropertyChanged();
}
}
public MainViewModel()
{
parameter1 = new object();
parameter2 = new object();
parameter3 = new object();
HomeVM = new HomeViewModel();
HomeVM.Initialize(parameter1, parameter2, parameter3);
UserVM = new UserViewModel();
CurrentView = HomeVM;
HomeViewCommand = new RelayCommand(o =>
{
CurrentView = HomeVM;
});
UserViewCommand = new RelayCommand(o =>
{
CurrentView = UserVM;
});
}
}
}
HomeViewModel.cs
namespace Practice.MVVM.ViewModel
{
public class HomeViewModel : ObservableObject
{
private object _parameter1;
private object _parameter2;
private object _parameter3;
public HomeViewModel()
{
}
public void Initialize(object parameter1, object parameter2, object parameter3)
{
_parameter1= parameter1;
_parameter2= parameter2;
_parameter3= parameter3;
}
}
}
Thank you in advance.

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>

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

WPF data templates

I'm getting started with WPF and trying to get my head around connecting data to the UI. I've managed to connect to a class without any issues, but what I really want to do is connect to a property of the main window.
Here's the XAML:
<Window x:Class="test3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:custom="clr-namespace:test3"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<CollectionViewSource
Source="{Binding Source={x:Static Application.Current}, Path=Platforms}"
x:Key="platforms"/>
<DataTemplate DataType="{x:Type custom:Platform}">
<StackPanel>
<CheckBox IsChecked="{Binding Path=Selected}"/>
<TextBlock Text="{Binding Path=Name}"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox ItemsSource="{Binding Source={StaticResource platforms}}"/>
</Grid>
Here's the code for the main window:
public partial class MainWindow : Window
{
ObservableCollection<Platform> m_platforms;
public MainWindow()
{
m_platforms = new ObservableCollection<Platform>();
m_platforms.Add(new Platform("PC"));
InitializeComponent();
}
public ObservableCollection<Platform> Platforms
{
get { return m_platforms; }
set { m_platforms = value; }
}
}
Here's the Platform class:
public class Platform
{
private string m_name;
private bool m_selected;
public Platform(string name)
{
m_name = name;
m_selected = false;
}
public string Name
{
get { return m_name; }
set { m_name = value; }
}
public bool Selected
{
get { return m_selected; }
set { m_selected = value; }
}
}
This all compiles and runs fine but the list box displays with nothing in it. If I put a breakpoint on the get method of Platforms, it doesn't get called. I don't understand as Platforms is what the XAML should be connecting to!
Your code looks ok apart from the fact that the Binding on Source on CollectionViewSource is not correct. You probably meant this:
<CollectionViewSource
Source="{Binding Source={x:Static Application.Current}, Path=MainWindow.Platforms}"
x:Key="platforms"/>
Without this change the Binding actually looked for property Platforms on Application instance.
I would suggest you add the platforms not to the MainWindow but rather set it as the MainWindow's DataContext (wrapped inside a ViewModel).
That way you can very easily bind against it (the binding code would look like ItemsSource={Binding Path=Platforms}).
This is part of WPFs design, that every form should have a explicit DataContext it binds to.
A somewhat more appropriate solution is to give your window a name. A nice convention is _this.
<Window x:Name="_this" x:Class="test3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:custom="clr-namespace:test3"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<CollectionViewSource
Source="{Binding ElementName=_this, Path=Platforms}"
x:Key="platforms"/>
<DataTemplate DataType="{x:Type custom:Platform}">
<StackPanel>
<CheckBox IsChecked="{Binding Path=Selected}"/>
<TextBlock Text="{Binding Path=Name}"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox ItemsSource="{Binding Source={StaticResource platforms}}"/>
</Grid>
Here's my updated code, the XAML:
<Window x:Name="_this"
x:Class="test3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:custom="clr-namespace:test3"
Title="MainWindow" Height="190" Width="177">
<Window.Resources>
<CollectionViewSource
Source="{Binding ElementName=_this, Path=Platforms}"
x:Key="platforms"/>
<DataTemplate x:Key="platformTemplate" DataType="{x:Type custom:Platform}">
<StackPanel Orientation="Horizontal">
<CheckBox Margin="1" IsChecked="{Binding Path=Selected}"/>
<TextBlock Margin="1" Text="{Binding Path=Name}"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="23" />
<RowDefinition Height="23" />
</Grid.RowDefinitions>
<ListBox Grid.Row="0"
ItemsSource="{Binding Source={StaticResource platforms}}"
ItemTemplate="{StaticResource platformTemplate}"/>
<Button Click="OnBuild" Grid.Row="1">Build...</Button>
<Button Click="OnTogglePC" Grid.Row="2">Toggle PC</Button>
</Grid>
</Window>
The code behind the XAML:
public partial class MainWindow : Window
{
ObservableCollection<Platform> m_platforms;
public MainWindow()
{
m_platforms = new ObservableCollection<Platform>();
m_platforms.Add(new Platform("PC"));
m_platforms.Add(new Platform("PS3"));
m_platforms.Add(new Platform("Xbox 360"));
InitializeComponent();
}
public ObservableCollection<Platform> Platforms
{
get { return m_platforms; }
set { m_platforms = value; }
}
private void OnBuild(object sender, RoutedEventArgs e)
{
string text = "";
foreach (Platform platform in m_platforms)
{
if (platform.Selected)
{
text += platform.Name + " ";
}
}
if (text == "")
{
text = "none";
}
MessageBox.Show(text, "WPF TEST");
}
private void OnTogglePC(object sender, RoutedEventArgs e)
{
m_platforms[0].Selected = !m_platforms[0].Selected;
}
}
...and finally the Platform code, enhanced to finish off the two way interaction:
public class Platform : INotifyPropertyChanged
{
private string m_name;
private bool m_selected;
public Platform(string name)
{
m_name = name;
m_selected = false;
}
public string Name
{
get { return m_name; }
set
{
m_name = value;
OnPropertyChanged("Name");
}
}
public bool Selected
{
get { return m_selected; }
set
{
m_selected = value;
OnPropertyChanged("Selected");
}
}
private void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
Using DataContext, it gets even easier!
<Window x:Class="test5.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:custom="clr-namespace:test5"
Title="MainWindow" Height="190" Width="177">
<Window.Resources>
<CollectionViewSource
Source="{Binding Path=.}"
x:Key="platforms"/>
<DataTemplate x:Key="platformTemplate" DataType="{x:Type custom:Platform}">
<StackPanel Orientation="Horizontal">
<CheckBox Margin="1" IsChecked="{Binding Path=Selected}"/>
<TextBlock Margin="1" Text="{Binding Path=Name}"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="23" />
<RowDefinition Height="23" />
</Grid.RowDefinitions>
<ListBox Grid.Row="0"
ItemsSource="{Binding Source={StaticResource platforms}}"
ItemTemplate="{StaticResource platformTemplate}"/>
<Button Click="OnBuild" Grid.Row="1">Build...</Button>
<Button Click="OnTogglePC" Grid.Row="2">Toggle PC</Button>
</Grid>
</Window>
Here's the code behind this:
private ObservableCollection<Platform> m_platforms;
public MainWindow()
{
InitializeComponent();
m_platforms = new ObservableCollection<Platform>();
m_platforms.Add(new Platform("PC"));
m_platforms.Add(new Platform("PS3"));
m_platforms.Add(new Platform("Xbox 360"));
DataContext = m_platforms;
}
public void OnBuild(object sender, RoutedEventArgs e)
{
string text = "";
foreach (Platform platform in m_platforms)
{
if (platform.Selected)
{
text += platform.Name + " ";
}
}
if (text == "")
{
text = "none";
}
MessageBox.Show(text, "WPF TEST");
}
public void OnTogglePC(object sender, RoutedEventArgs e)
{
m_platforms[0].Selected = !m_platforms[0].Selected;
}
Note that I've dropped the need to declare Platforms as a property of the main window, instead I assign it to the DataContext, and the XAML source becomes, simply, "."

Categories