I am writing a wpf application and implements mvvm light tool. The GUI looks like:
Every time when a user click on button, it should change the content on the right side, marked with red border. The XAML code:
<igWpf:XamRibbonWindow x:Class="BackupCustomizing.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:ignore="http://www.ignore.com"
xmlns:ig="http://schemas.infragistics.com/xaml"
xmlns:views="clr-namespace:BackupCustomizing.Views"
xmlns:igWpf="http://schemas.infragistics.com/xaml/wpf"
mc:Ignorable="d ignore"
Height="400"
Width="700"
Title="Backup customizing V0.1"
DataContext="{Binding Main, Source={StaticResource Locator}}" ResizeMode="NoResize">
<igWpf:XamRibbonWindow.Resources>
<DataTemplate DataType="{x:Type views:ServerView}"></DataTemplate>
</igWpf:XamRibbonWindow.Resources>
<ig:ThemeManager.Theme>
<ig:Office2013Theme />
</ig:ThemeManager.Theme>
<igWpf:RibbonWindowContentHost x:Name="_content"
Theme="Office2013"
igWpf:RibbonWindowContentHost.ApplicationAccentColor="#0072C6">
<Grid x:Name="LayoutRoot">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<views:NavigationView Grid.Column="0"/>
<ContentPresenter Content="{Binding}" Grid.Column="1"/>
</Grid>
</igWpf:RibbonWindowContentHost>
</igWpf:XamRibbonWindow>
and the code behind:
using System.Windows;
using BackupCustomizing.ViewModel;
using Infragistics.Themes;
using Infragistics.Windows.Ribbon;
namespace BackupCustomizing
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : XamRibbonWindow
{
/// <summary>
/// Initializes a new instance of the MainWindow class.
/// </summary>
public MainWindow()
{
InitializeComponent();
Closing += (s, e) => ViewModelLocator.Cleanup();
}
}
}
As you can see the code above, I tried with:
<igWpf:XamRibbonWindow.Resources>
<DataTemplate DataType="{x:Type views:ServerView}"></DataTemplate>
</igWpf:XamRibbonWindow.Resources>
and the content presenter:
<ContentPresenter Content="{Binding}" Grid.Column="1"/>
and here I stocked, how to continue?
The ViewModel code:
using BackupCustomizing.Model;
using GalaSoft.MvvmLight;
namespace BackupCustomizing.ViewModel
{
/// <summary>
/// This class contains properties that the main View can data bind to.
/// <para>
/// See http://www.galasoft.ch/mvvm
/// </para>
/// </summary>
public class MainViewModel : ViewModelBase
{
private readonly IDataService _dataService;
private string _welcomeTitle = string.Empty;
/// <summary>
/// Initializes a new instance of the MainViewModel class.
/// </summary>
public MainViewModel(IDataService dataService)
{
_dataService = dataService;
_dataService.GetData(
(item, error) =>
{
});
}
}
}
To get you code working in minimum changes
public class MainViewModel : ViewModelBase
{
private readonly IDataService _dataService;
private string _welcomeTitle = string.Empty;
private ViewModelBase detailsViewModel = null;
public ViewModelBase DetailsViewModel{
get { return detailsViewModel;}
set { detailsViewModel = value; RaisePropertyChanged("DetailsViewModel"); }
}
/// <summary>
/// Initializes a new instance of the MainViewModel class.
/// </summary>
public MainViewModel(IDataService dataService)
{
_dataService = dataService;
_dataService.GetData(
(item, error) =>
{
detailsViewModel = new ServerViewModel(item); //ViewModel for the ServerView
});
}
}
<igWpf:XamRibbonWindow.Resources>
<DataTemplate DataType="{x:Type viewModel:ServerViewModel}">
<views:ServerView />
</DataTemplate>
</igWpf:XamRibbonWindow.Resources>
<ContentPresenter Content="{Binding DetailsViewModel}" Grid.Column="1"/>
There are other techniques to do MVVM, I am just showing the way to do it with the approach you have started with. Problem with this approach is that it will not scale well to large number of views in the ContentPresenter.
Related
I know this question has been posted already, but I don't understand the answers. In My case I have a project with only a UserControl:
<UserControl x:Class="SimpleOxyPlotUserControl.UserControl1View"
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:SimpleOxyPlotUserControl"
xmlns:oxy="http://oxyplot.org/wpf"
mc:Ignorable="d"
x:Name="uc1"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<oxy:PlotView Model="{Binding ElementName=uc1, Path=OxyPlotModel, Mode=TwoWay}"/>
</Grid>
</UserControl>
and its code behind:
using System.Windows;
using System.Windows.Controls;
using OxyPlot;
namespace SimpleOxyPlotUserControl
{
/// <summary>
/// Interaction logic for UserControl1View.xaml
/// </summary>
public partial class UserControl1View : UserControl
{
public UserControl1View() => InitializeComponent();
public PlotModel OxyPlotModel
{
get { return (PlotModel)GetValue(OxyPlotModelProperty); }
set { SetValue(OxyPlotModelProperty, value); }
}
public static readonly DependencyProperty OxyPlotModelProperty =
DependencyProperty.Register("OxyPlotModel", typeof(PlotModel), typeof(UserControl1View),
new PropertyMetadata(new PlotModel()));
}
}
Then I have another project with a simple WPF App and its MainWindow.xaml:
<Window x:Class="SimpleOxyPlotUserControlApp.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:simpleOxyPlotUserControl="clr-namespace:SimpleOxyPlotUserControl;assembly=SimpleOxyPlotUserControl"
Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<simpleOxyPlotUserControl:UserControl1View/>
<simpleOxyPlotUserControl:UserControl1View Grid.Row="1"/>
</Grid>
</Window>
and its code behind:
using System.Windows;
namespace SimpleOxyPlotUserControlApp.Views
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}
I receive the error message "InvalidOperationException: This PlotModel is already in use by some other PlotView control."
I know this is, because each Oxy PlotModel can only be connected to exactly one Oxy PlotView. But why does it not create a new Oxy PlotView and PlotModel each time, when I insert my UserControl? And how can I ensure that it does so?
OxyPlotModelProperty is registered with new PlotModel() default value in PropertyMetadata. but OxyPlotModelProperty is static so you get the same PlotModel instance for all UserControl1View instances.
fix it by creating PlotModel in constructor:
public partial class UserControl1View : UserControl
{
public UserControl1View()
{
InitializeComponent();
SetLocalValue(OxyPlotModelProperty, new PlotModel());
}
public PlotModel OxyPlotModel
{
get { return (PlotModel)GetValue(OxyPlotModelProperty); }
set { SetValue(OxyPlotModelProperty, value); }
}
public static readonly DependencyProperty OxyPlotModelProperty =
DependencyProperty.Register("OxyPlotModel", typeof(PlotModel), typeof(UserControl1View),
new PropertyMetadata(null));
}
I have a feeling my base issue with this problem is binding some internal properties.
In the usercontrol I have a rectangle that contains a linear gradient. I created a dependency property to be able to give a value (0 to 1) to specify where the gradient line should be in the rectangle. To adjust this value dynamically I connected a slider to it for testing.
I also added some feedback textblocks to let me know how some of the data is flowing and if it is flowing. And from that I can tell my binding to my GradientStops.offset values from my user control properties are not working. How do I get the user control to update the rectangle gradient by just changing RectangleLevel.RectLevel value?
USERCONTROL XAML
<UserControl x:Class="RectDynamicGradient.RectangleLevel"
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:RectDynamicGradient"
xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
mc:Ignorable="d"
d:DesignHeight="180" d:DesignWidth="80">
<Grid>
<Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Rectangle.Fill>
<LinearGradientBrush>
<GradientStop Color="Cyan" Offset="{Binding Gradient_top_color, diag:PresentationTraceSources.TraceLevel=High}"/>
<GradientStop Color="Black" Offset="{Binding Gradient_bottom_color, diag:PresentationTraceSources.TraceLevel=High}"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
</Grid>
</UserControl>
USERCONTROL CODE
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace RectDynamicGradient
{
/// <summary>
/// Interaction logic for RectangleLevel.xaml
/// </summary>
public partial class RectangleLevel : UserControl
{
public double Gradient_bottom_color { get; set; }
public double Gradient_top_color { get; set; }
public double RectLevel
{
get { return (double)GetValue(RectLevelProperty); }
set { SetValue(RectLevelProperty, value); }
}
// Using a DependencyProperty as the backing store for RectLevel. This enables animation, styling, binding, etc...
public static readonly DependencyProperty RectLevelProperty =
DependencyProperty.Register("RectLevel", typeof(double), typeof(RectangleLevel), new FrameworkPropertyMetadata(0.0,
FrameworkPropertyMetadataOptions.AffectsRender,
new PropertyChangedCallback(ChangeLevel),
new CoerceValueCallback(CoerceLevel)),
new ValidateValueCallback(ValidateLevel));
public static void ChangeLevel(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
RectangleLevel t = d as RectangleLevel;
t.UpdateGradientStops((double)e.NewValue);
}
public static object CoerceLevel(DependencyObject d, object value)
{
if (value is string valstring)
{
if (Double.TryParse(valstring, out double lvl))
{
return lvl;
}
}
if (value is double valdouble)
{
return valdouble;
}
throw new Exception();
}
public static bool ValidateLevel(object value)
{
double? level = 0;
if (value is string valstring)
{
if (Double.TryParse(valstring, out double lvl))
{
level = lvl;
}
}
if (value is double valdouble)
{
level = valdouble;
}
if (level.HasValue && level >= 0 && level <= 1)
return true;
else
return false;
}
public RectangleLevel()
{
InitializeComponent();
this.DataContext = this;
}
private void UpdateGradientStops(double level)
{
double scale = 0;
if (level < .5)
{
scale = level;
}
else if (level >= .5)
{
scale = 1 - level;
}
Gradient_top_color = level;
Gradient_bottom_color = level + (level * .1 * scale);
}
}
}
MAINWINDOW XAML
<Window x:Class="RectDynamicGradient.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:RectDynamicGradient"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Slider x:Name="TheSlider" RenderTransformOrigin="0.5,0.5" VerticalAlignment="Center" LargeChange="0.1" Maximum="1" SmallChange="0.01" >
<Slider.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleY="2"/>
<SkewTransform/>
<RotateTransform/>
<TranslateTransform Y="2"/>
</TransformGroup>
</Slider.RenderTransform>
</Slider>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal" Grid.Column="0">
<TextBlock Text="SliderValue:"/>
<TextBlock HorizontalAlignment="Center" FontSize="32" Text="{Binding ElementName=TheSlider, Path=Value}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Grid.Column="1">
<TextBlock Text="UC LEVEL Value:"/>
<TextBlock Grid.Row="1" HorizontalAlignment="Center" FontSize="32" Text="{Binding ElementName=MyUserControl, Path=RectLevel}"/>
</StackPanel>
</Grid>
<StackPanel Orientation="Horizontal" Grid.Row="2" HorizontalAlignment="Center">
<local:RectangleLevel x:Name="MyUserControl" Width="80" VerticalAlignment="Stretch" RectLevel="{Binding ElementName=TheSlider, Path=Value}"/>
</StackPanel>
</Grid>
</Window>
MAINWINDOW CODE
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace RectDynamicGradient
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}
EDIT
After feedback from the comments the improvments were made.
DataContext removed this and added relative source to user control (named "uc")
Changed NotifyProperties into DependencyProperties
Replaced DataContext
<GradientStop Color="Cyan" Offset="{Binding ElementName=uc, Path=GradientFilledColor}"/>
Updated Dependency Properties
/// <summary>
/// Property to change the GradientEmptyColor (GradientStop offset) of the rectangle by interfacing with the dependency property.
/// </summary>
public double GradientEmptyColor
{
get { return (double)GetValue(GradientEmptyColorProperty); }
set { SetValue(GradientEmptyColorProperty, value); }
}
/// <summary>
/// Using a DependencyProperty as the backing store for GradientEmptyColor. This enables animation, styling, binding, etc...
/// </summary>
public static readonly DependencyProperty GradientEmptyColorProperty =
DependencyProperty.Register("GradientEmptyColor", typeof(double), typeof(RectangleLevel), new PropertyMetadata(0.0));
/// <summary>
/// Property to change the GradientFilledColor (GradientStop offset) of the rectangle by interfacing with the dependency property.
/// </summary>
public double GradientFilledColor
{
get { return (double)GetValue(GradientFilledColorProperty); }
set { SetValue(GradientFilledColorProperty, value); }
}
/// <summary>
/// Using a DependencyProperty as the backing store for GradientFilledColor. This enables animation, styling, binding, etc...
/// </summary>
public static readonly DependencyProperty GradientFilledColorProperty =
DependencyProperty.Register("GradientFilledColor", typeof(double), typeof(RectangleLevel), new PropertyMetadata(0.0));
The DependencyProperty provides the same capability as the INotifyProperty PropertChanged event (uses a different mechanism though).
As mentioned by #Clemens setting datacontext to self is a terrible idea and would break linking when the user control is used through-out the project and datacontext is set to something else.
I am still open to suggestions that help support good practices and learning.
UPDATED CODE
USERCONTROL XAML
<UserControl x:Class="RectDynamicGradient.RectangleLevel"
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:RectDynamicGradient"
mc:Ignorable="d"
d:DesignHeight="180" d:DesignWidth="80" x:Name="uc">
<Grid>
<Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Rectangle.Fill>
<LinearGradientBrush StartPoint=".5,1" EndPoint=".5,0">
<GradientStop Color="Cyan" Offset="{Binding ElementName=uc, Path=GradientFilledColor}"/>
<GradientStop Color="Black" Offset="{Binding ElementName=uc, Path=GradientEmptyColor}"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
</Grid>
</UserControl>
USERCONTROL CODE
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
namespace RectDynamicGradient
{
/// <summary>
/// Interaction logic for RectangleLevel.xaml
/// </summary>
public partial class RectangleLevel : UserControl
{
/// <summary>
/// Property to change the GradientEmptyColor (GradientStop offset) of the rectangle by interfacing with the dependency property.
/// </summary>
public double GradientEmptyColor
{
get { return (double)GetValue(GradientEmptyColorProperty); }
set { SetValue(GradientEmptyColorProperty, value); }
}
/// <summary>
/// Using a DependencyProperty as the backing store for GradientEmptyColor. This enables animation, styling, binding, etc...
/// </summary>
public static readonly DependencyProperty GradientEmptyColorProperty =
DependencyProperty.Register("GradientEmptyColor", typeof(double), typeof(RectangleLevel), new PropertyMetadata(0.0));
/// <summary>
/// Property to change the GradientFilledColor (GradientStop offset) of the rectangle by interfacing with the dependency property.
/// </summary>
public double GradientFilledColor
{
get { return (double)GetValue(GradientFilledColorProperty); }
set { SetValue(GradientFilledColorProperty, value); }
}
/// <summary>
/// Using a DependencyProperty as the backing store for GradientFilledColor. This enables animation, styling, binding, etc...
/// </summary>
public static readonly DependencyProperty GradientFilledColorProperty =
DependencyProperty.Register("GradientFilledColor", typeof(double), typeof(RectangleLevel), new PropertyMetadata(0.0));
/// <summary>
/// Property to change the level (GradientStop offsets) of the rectangle by interfacing with the dependency property.
/// </summary>
public double RectLevel
{
get { return (double)GetValue(RectLevelProperty); }
set { SetValue(RectLevelProperty, value); }
}
/// <summary>
/// Using a DependencyProperty as the backing store for RectLevel. This enables animation, styling, binding, etc...
/// </summary>
public static readonly DependencyProperty RectLevelProperty =
DependencyProperty.Register("RectLevel", typeof(double), typeof(RectangleLevel), new FrameworkPropertyMetadata(0.0,
FrameworkPropertyMetadataOptions.AffectsRender,
new PropertyChangedCallback(ChangeLevel),
new CoerceValueCallback(CoerceLevel)),
new ValidateValueCallback(ValidateLevel));
/// <summary>
/// PropertyChangedCallback for DependencyProperty RectLevelProperty.
/// </summary>
/// <param name="d">Dependency object causing the event.</param>
/// <param name="e">Change event arguments.</param>
public static void ChangeLevel(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
RectangleLevel t = d as RectangleLevel;
t.UpdateGradientStops((double)e.NewValue);
}
/// <summary>
/// CoerceValueCallback for DependencyProperty RectLevelProperty.
/// </summary>
/// <param name="d">Dependency object causing the event.</param>
/// <param name="value">Value being sent to RectLevelProperty be coerced.</param>
/// <returns></returns>
public static object CoerceLevel(DependencyObject d, object value)
{
if (value is string valstring)
{
if (Double.TryParse(valstring, out double lvl))
{
return lvl;
}
}
if (value is double valdouble)
{
return valdouble;
}
throw new Exception();
}
/// <summary>
/// ValidateValueCallback for DependencyProperty RectLevelProperty
/// </summary>
/// <param name="value">Value being sent to RectLevelProperty be validated.</param>
/// <returns>True, if valid value between and including 0 and 1.</returns>
public static bool ValidateLevel(object value)
{
double? level = 0;
if (value is string valstring)
{
if (Double.TryParse(valstring, out double lvl))
{
level = lvl;
}
}
if (value is double valdouble)
{
level = valdouble;
}
if (level.HasValue && level >= 0 && level <= 1)
return true;
else
return false;
}
/// <summary>
/// Constructor sets DataContext to itself.
/// </summary>
public RectangleLevel()
{
InitializeComponent();
this.DataContext = this;
}
/// <summary>
/// Updates the variables binded to the GradientStops for the rectangle.
/// </summary>
/// <param name="level">Level where the GradientStops should be. Valid value is 0 to 1 representing 0 to 100% filled.</param>
private void UpdateGradientStops(double level)
{
double scale = 0;
if (level < .5)
{
scale = level;
}
else if (level >= .5)
{
scale = 1 - level;
}
GradientFilledColor = level;
GradientEmptyColor = level + (level * .1 * scale);
}
}
}
MAINWINDOW XAML
<Window x:Class="RectDynamicGradient.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:RectDynamicGradient"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Slider x:Name="TheSlider" RenderTransformOrigin="0.5,0.5" VerticalAlignment="Center" LargeChange="0.1" Maximum="1" SmallChange="0.01" >
<Slider.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleY="2"/>
<SkewTransform/>
<RotateTransform/>
<TranslateTransform Y="2"/>
</TransformGroup>
</Slider.RenderTransform>
</Slider>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal" Grid.Column="0">
<TextBlock Text="SliderValue:"/>
<TextBlock HorizontalAlignment="Center" FontSize="32" Text="{Binding ElementName=TheSlider, Path=Value}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Grid.Column="1">
<TextBlock Text="UC LEVEL Value:"/>
<TextBlock Grid.Row="1" HorizontalAlignment="Center" FontSize="32" Text="{Binding ElementName=MyUserControl, Path=RectLevel}"/>
</StackPanel>
</Grid>
<StackPanel Orientation="Horizontal" Grid.Row="2" HorizontalAlignment="Center">
<local:RectangleLevel x:Name="MyUserControl" Width="80" VerticalAlignment="Stretch" RectLevel="{Binding ElementName=TheSlider, Path=Value}"/>
</StackPanel>
</Grid>
</Window>
Hi im working on an Wpf MVVM project, but i can't figure it out how to bind a button with the Command attribute in Xaml to a RelayCommand in the Viewmodel, i found multiple answer's online but i don't get it (implementing the ICommand interface and canexecute and stuff..) , the thing is that i have an already created project made by other developers where they simply , bind the button in Xaml like so :
heres the full code in the View :
<Window x:Class="MvvmLight3.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:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:mvvm="http://www.galasoft.ch/mvvmlight"
xmlns:ignore="http://www.galasoft.ch/ignore"
mc:Ignorable="d ignore"
Height="300"
Width="300"
Title="MVVM Light Application"
DataContext="{Binding Main, Source={StaticResource Locator}}">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Skins/MainSkin.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid x:Name="LayoutRoot">
<TextBlock FontSize="36"
FontWeight="Bold"
Foreground="Purple"
VerticalAlignment="Center"
HorizontalAlignment="Center"
TextWrapping="Wrap" ><Run Text="WelcomeTitle"/><InlineUIContainer>
<Label Content="{Binding welcome}"/>
</InlineUIContainer></TextBlock>
<Button Content="Button" Command="{Binding The_Command}" HorizontalAlignment="Left" Margin="170,80,0,0" VerticalAlignment="Top" Width="75">
</Button>
</Grid>
and in the ViewModel the full code is :
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.CommandWpf;
using MvvmLight3.Model;
using System.Windows.Input;
namespace MvvmLight3.ViewModel
{
/// <summary>
/// This class contains properties that the main View can data bind to.
/// <para>
/// See http://www.mvvmlight.net
/// </para>
/// </summary>
public class MainViewModel : ViewModelBase
{
private readonly IDataService _dataService;
/// <summary>
/// The <see cref="WelcomeTitle" /> property's name.
/// </summary>
public const string WelcomeTitlePropertyName = "WelcomeTitle";
private string _welcomeTitle = string.Empty;
private string _welcome = "this work";
/// <summary>
/// Gets the WelcomeTitle property.
/// Changes to that property's value raise the PropertyChanged event.
/// </summary>
public string WelcomeTitle
{
get
{
return _welcomeTitle;
}
set
{
Set(ref _welcomeTitle, value);
}
}
public string welcome
{
get
{
return _welcome;
}
set
{
Set(ref _welcome, value);
}
}
/// <summary>
/// Initializes a new instance of the MainViewModel class.
/// </summary>
public MainViewModel(IDataService dataService)
{
_dataService = dataService;
_dataService.GetData(
(item, error) =>
{
if (error != null)
{
// Report error here
return;
}
WelcomeTitle = item.Title;
});
}
private RelayCommand _The_Command;
public RelayCommand The_Command
{
get
{
return _The_Command
?? (_The_Command = new RelayCommand(
() =>
{
//some Code
}));
}
}
////public override void Cleanup()
////{
//// // Clean up if needed
//// base.Cleanup();
////}
}
}
in my case the execution dont enter the RelayCommand.
the code in the ViewModelLocator.cs
/*
In App.xaml:
<Application.Resources>
<vm:ViewModelLocatorTemplate xmlns:vm="clr-namespace:MvvmLight3.ViewModel"
x:Key="Locator" />
</Application.Resources>
In the View:
DataContext="{Binding Source={StaticResource Locator}, Path=ViewModelName}"
*/
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Ioc;
using Microsoft.Practices.ServiceLocation;
using MvvmLight3.Model;
namespace MvvmLight3.ViewModel
{
/// <summary>
/// This class contains static references to all the view models in the
/// application and provides an entry point for the bindings.
/// <para>
/// See http://www.mvvmlight.net
/// </para>
/// </summary>
public class ViewModelLocator
{
static ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
if (ViewModelBase.IsInDesignModeStatic)
{
SimpleIoc.Default.Register<IDataService, Design.DesignDataService>();
}
else
{
SimpleIoc.Default.Register<IDataService, DataService>();
}
SimpleIoc.Default.Register<MainViewModel>();
}
/// <summary>
/// Gets the Main property.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
"CA1822:MarkMembersAsStatic",
Justification = "This non-static member is needed for data binding purposes.")]
public MainViewModel Main
{
get
{
return ServiceLocator.Current.GetInstance<MainViewModel>();
}
}
/// <summary>
/// Cleans up all the resources.
/// </summary>
public static void Cleanup()
{
}
}
}
the project is MVVMLight (wpf451) template .
Thank you.
It worked, it's something silly, actually if you notice my relay Command (The_Command) was empty and although it had a comment inside it (//some Code), a breakpoint on the get or return never went through when I click the button.
Solution:
after I added a functioning code inside the Command it worked: added for example a simple MessageBox.Show. so the Binding was correct. For the info I'm using the VisualStudio Enterprise 2017 .
I am using MVVMLight framework and I try to use dependency injection when I instanciate a View. When the View is extracted from the service locator SimpleIoc, I want that SimpleIoc creates a new instance of this View and pass as parameter its ViewModel. How should I register the View with its ViewModel in the SimpleIoC to get this approach? At the moment I do the register like that:
SimpleIoc.Default.Register<MainViewModel>();
But I am try to do that:
SimpleIoc.Default.Register<IMainView, MainViewModel>();
assuming that the MainViewModel is a new instance of the view model for MainView (that implements the interface IMainView).
How do I have to register View and ViewModel in my SimpleIoC and how do I have to call the View applying dependency injection with its references (with its ViewModel)?
Update:
My current code:
ViewModelLocator.cs
public class ViewModelLocator
{
static ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
if (ViewModelBase.IsInDesignModeStatic)
{
SimpleIoc.Default.Register<IDataService, Design.DesignDataService>();
}
else
{
SimpleIoc.Default.Register<IDataService, DataService>();
}
SimpleIoc.Default.Register<MainViewModel>();
SimpleIoc.Default.Register<NewsViewModel>();
SimpleIoc.Default.Register<TeamsViewModel>();
}
/// <summary>
/// Gets the Main property.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
"CA1822:MarkMembersAsStatic",
Justification = "This non-static member is needed for data binding purposes.")]
public MainViewModel Main
{
get
{
return ServiceLocator.Current.GetInstance<MainViewModel>();
}
}
public NewsViewModel NewsViewModel
{
get
{
return ServiceLocator.Current.GetInstance<NewsViewModel>();
}
}
public TeamsViewModel TeamsViewModel
{
get
{
return ServiceLocator.Current.GetInstance<TeamsViewModel>();
}
}
/// <summary>
/// Cleans up all the resources.
/// </summary>
public static void Cleanup()
{
}
}
MainWindow.xaml
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Skins/MainSkin.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal">
<Button Height="30" Width="40" Command="{Binding OpenNews}">News</Button>
<Button Height="30" Width="40" Margin="5,0,0,0" Command="{Binding OpenTeams}">Teams</Button>
</StackPanel>
<ContentPresenter Grid.Row="1" Content="{Binding CurrentViewModel}"/>
</Grid>
MainViewModel.cs
private ViewModelBase _currentViewModel;
/// <summary>
/// Contains the current view model.
/// </summary>
public ViewModelBase CurrentViewModel
{
get { return _currentViewModel; }
set
{
if (_currentViewModel == value)
return;
_currentViewModel = value;
RaisePropertyChanged(CurrentViewModelPropertyName);
}
}
#region Commands
public RelayCommand OpenNews { get; set; }
public RelayCommand OpenTeams { get; set; }
#endregion
private void RegisterCommands()
{
OpenNews = new RelayCommand(ShowOpenNews);
OpenTeams = new RelayCommand(ShowOpenTeams);
}
private void ShowOpenTeams()
{
CurrentViewModel = ServiceLocator.Current.GetInstance<TeamsViewModel>(); //MainViewModel.TeamsViewModel;
}
private void ShowOpenNews()
{
CurrentViewModel = ServiceLocator.Current.GetInstance<NewsViewModel>();
}
App.xaml
<Application.Resources>
<DataTemplate DataType="{x:Type vm:NewsViewModel}">
<view:NewsView/>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:TeamsViewModel}">
<view:TeamView/>
</DataTemplate>
<!--Global View Model Locator-->
<vm:ViewModelLocator x:Key="Locator"
d:IsDataSource="True" />
</Application.Resources>
At the moment I change the current view through DataTemplates and change the property CurrentViewModel. (Now ViewModel firstandI want View first)
I want to do Dependency Injection and use the SimpleIOC but I don't know how to put into play that.
I have a combobox in a tab item in MVVM. This tab can be created multiple times in my application (same view, same view model but different instance), so I can switch from one tab to another (but they are tab of the same type).
It works perfectly with every WPF control, but with combobox I have a strange behaviour:
the focus tab, when it loses focus, gets the selected item of the combox box of the tab that the application is focusing on.
If I switch from 2 tabs that are not of the same type everything works correctly, any idea about that? Thanks.
XAML:
<CollectionViewSource x:Key="StatusView" Source="{Binding Path=StatusList}"/>
<ComboBox Name="_spl2Status" Grid.Column="3" Grid.Row="0"
ItemsSource="{Binding Source={StaticResource StatusView}}"
SelectedValue="{Binding Path=CurrentSPL2.ID_SPL2_STATUS, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedValuePath="FL_TYPE"
DisplayMemberPath="ID_TYPE">
</ComboBox>
VM:
public List<NullableByteEnumType> StatusList
{
get
{
return (SPC_SPL2.SPL2StatusCollection.Skip(1)).ToList();
}
}
private SPC_SPL2 _currentSPL2 = null;
public SPC_SPL2 CurrentSPL2
{
get
{
if (_currentSPL2== null)
Controller.Execute(delegate(IResult result)
{
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("FL_ACTIVE", true);
parameters.Add("ID_SPL2", _itemcode);
Model.Invalidate(typeof(SPC_SPL2), Filter.GENERIC<SPC_SPL2>(parameters, "ID_SPL2"));
Model.Include<SPC_SPL2>();
if (Model.Appendload(result) == false)
return false;
Debug.Assert(Context.SPC_SPL2.Count == 1);
_currentSPL2= Context.SPC_SPL2.FirstOrDefault();
return result.Successful;
});
return _currentSPL2;
}
set
{
_currentSPL2= value;
OnPropertyChanged(() => CurrentSPL2);
}
}
my tabs are handled in this way:
<Grid>
<Border Grid.Row="0">
<ContentControl
Content="{Binding Path=Workspaces}"
ContentTemplate="{StaticResource MasterWorkspacesTemplate}"
/>
</Border>
</Grid>
where
<DataTemplate x:Key="MasterWorkspacesTemplate">
<TabControl IsSynchronizedWithCurrentItem="True"
BorderThickness="0"
ItemsSource="{Binding}"
SelectedItem="{Binding}"
ItemContainerStyleSelector="{StaticResource TabItemTemplate}"
/>
</DataTemplate>
and workspaces (my viewmodels list) (T is a class who inherit from viewModelBase)
public T CurrentWorkspace
{
get { return WorkspacesView.CurrentItem as T; }
}
private ObservableCollection<T> _workspaces;
public ObservableCollection<T> Workspaces
{
get
{
if (_workspaces == null)
{
_workspaces = new ObservableCollection<T>();
_workspaces.CollectionChanged += _OnWorkspacesChanged;
}
return _workspaces;
}
}
protected ICollectionView WorkspacesView
{
get
{
ICollectionView collectionView = CollectionViewSource.GetDefaultView(Workspaces);
Debug.Assert(collectionView != null);
return collectionView;
}
}
I have recreated your problem. But I couldn't find any issue. Please look at the code below and you might get the solustion. Here is my solution.
MyTab view model
public class MyTab : ViewModelBase
{
#region Declarations
private ObservableCollection<string> statusList;
private string selectedStatus;
#endregion
#region Properties
/// <summary>
/// Gets or sets the header.
/// </summary>
/// <value>The header.</value>
public string Header { get; set; }
/// <summary>
/// Gets or sets the content.
/// </summary>
/// <value>The content.</value>
public string Content { get; set; }
/// <summary>
/// Gets or sets the status list.
/// </summary>
/// <value>The status list.</value>
public ObservableCollection<string> StatusList
{
get
{
return statusList;
}
set
{
statusList = value;
NotifyPropertyChanged("StatusList");
}
}
/// <summary>
/// Gets or sets the selected status.
/// </summary>
/// <value>The selected status.</value>
public string SelectedStatus
{
get
{
return selectedStatus;
}
set
{
selectedStatus = value;
NotifyPropertyChanged("SelectedStatus");
}
}
#endregion
}
MainViewModel view model
public class MainViewModel : ViewModelBase
{
#region Declarations
private ObservableCollection<MyTab> tabs;
private MyTab selectedTab;
#endregion
#region Properties
/// <summary>
/// Gets or sets the tabs.
/// </summary>
/// <value>The tabs.</value>
public ObservableCollection<MyTab> Tabs
{
get
{
return tabs;
}
set
{
tabs = value;
NotifyPropertyChanged("Tabs");
}
}
/// <summary>
/// Gets or sets the selected tab.
/// </summary>
/// <value>The selected tab.</value>
public MyTab SelectedTab
{
get
{
return selectedTab;
}
set
{
selectedTab = value;
NotifyPropertyChanged("SelectedTab");
}
}
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="MainViewModel"/> class.
/// </summary>
public MainViewModel()
{
this.Tabs = new ObservableCollection<MyTab>();
MyTab tab1 = new MyTab();
tab1.Header = "tab1";
tab1.Content = "Tab 1 content";
ObservableCollection<string> tab1StatusList = new ObservableCollection<string>();
tab1StatusList.Add("tab1 item1");
tab1StatusList.Add("tab1 item2");
tab1StatusList.Add("tab1 item3");
tab1.StatusList = tab1StatusList;
tab1.SelectedStatus = tab1StatusList.First();
this.Tabs.Add(tab1);
MyTab tab2 = new MyTab();
tab2.Header = "tab2";
tab2.Content = "Tab 2 content";
ObservableCollection<string> tab2StatusList = new ObservableCollection<string>();
tab2StatusList.Add("tab2 item1");
tab2StatusList.Add("tab2 item2");
tab2StatusList.Add("tab2 item3");
tab2.StatusList = tab2StatusList;
tab2.SelectedStatus = tab2StatusList.First();
this.Tabs.Add(tab2);
this.SelectedTab = tab1;
}
#endregion
}
And finally this is my XAML
<Window x:Class="ComboboxSelectedItem.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:viewModel="clr-namespace:ComboboxSelectedItem.ViewModels"
Title="MainWindow" Height="350" Width="525">
<Grid Name="mainGrid">
<Grid.DataContext>
<viewModel:MainViewModel />
</Grid.DataContext>
<TabControl
ItemsSource="{Binding Tabs, Mode=TwoWay}"
SelectedItem="{Binding SelectedTab}">
<TabControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Header}" Margin="0 0 20 0"/>
</StackPanel>
</DataTemplate>
</TabControl.ItemTemplate>
<!--Content section-->
<TabControl.ContentTemplate>
<DataTemplate>
<Grid>
<StackPanel Orientation="Vertical">
<TextBlock
Text="{Binding Content}" />
<ComboBox
ItemsSource="{Binding StatusList}"
SelectedItem="{Binding SelectedStatus}" />
</StackPanel>
</Grid>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
</Window>
Are you absolutely sure that you are creating a new instance of the viewmodel. If not, then the comboboxes are sharing the same collectionviewsource which means that a change in one combobox will be reflected in all comboboxes. I had this same problem myself.
Try declaring the collection view source in code:
CollectionViewSource StatusListViewSource = new CollectionViewSource();
StatusListViewSource.Source = SPL2StatusCollection;
then in xaml change binding to the collectionviewsource:
ItemsSource="{Binding StatusListViewSource.View}"
I converted from vb so it might need some edits.
Does that help?