Im just starting out with MVVM and at the moment still find alot of things confusing.
So I am trying to keep things as simple as I can at the moment.
I am trying to write code for a custom image which later will be able to be placed on a canvas control by a user at runtime. I'm trying to use MVVM so that I will be able to save and reload the content on a canvas.
I have created a model class called CustomImage with the following code:
namespace StoryboardToolMvvm
{
public class CustomImage
{
public Uri imageLocation { get; set; }
public BitmapImage bitmapImage { get; set; }
}
}
I have a modelview class as follows:
namespace StoryboardToolMvvm
{
class CustomImageViewModel : ViewModelBase
{
private CustomImage _customImage;
private ObservableCollection<CustomImage> _customImages;
private ICommand _SubmitCommand;
public CustomImage CustomImage
{
get { return _customImage; }
set
{
_customImage = value;
NotifyPropertyChanged("CustomImage");
}
}
public ObservableCollection<CustomImage> CustomImages
{
get { return _customImages; }
set
{
_customImages = value;
NotifyPropertyChanged("CustomImages");
}
}
public ICommand SubmitCommand
{
get
{
if (_SubmitCommand == null)
{
_SubmitCommand = new RelayCommand(param => this.Submit(), null);
}
return _SubmitCommand;
}
}
public CustomImageViewModel()
{
CustomImage = new CustomImage();
CustomImages = new ObservableCollection<CustomImage>();
CustomImages.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(CustomImages_CollectionChanged);
}
private void CustomImages_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
NotifyPropertyChanged("CustomImages");
}
private void Submit()
{
CustomImage.imageLocation = new Uri(#"H:\My Pictures\whale.png");
CustomImage.bitmapImage = new BitmapImage(CustomImage.imageLocation);
CustomImages.Add(CustomImage);
CustomImage = new CustomImage();
}
}
}
And a view class:
<UserControl x:Class="StoryboardToolMvvm.CustomImageView"
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:viewmodel="clr-namespace:StoryboardToolMvvm"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
<viewmodel:CustomImageViewModel x:Key="CustomImageViewModel"/>
</UserControl.Resources>
<Grid DataContext="{Binding Source={StaticResource CustomImageViewModel}}">
<Image Source="{Binding CustomImage.bitmapImage, Mode=TwoWay}" Width="150" Height="150" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="75,50,0,0" />
<Button Content="Submit" Command="{Binding SubmitCommand}" Width="100" Height="50" HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="0,0,0,20" />
</Grid>
</UserControl>
I add this view to my MainWindow.xaml
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:StoryboardToolMvvm" x:Class="StoryboardToolMvvm.MainWindow"
Title="MainWindow" Height="350" Width="525">
<Grid>
<local:CustomImageView HorizontalAlignment="Left" Height="100" Margin="181,110,0,0" VerticalAlignment="Top" Width="100"/>
</Grid>
</Window>
I am very unsure as to whether I am on the right lines here with a MVVM pattern so any comments would be much appreciated. Also when Submit is pressed I would have expected my image to load but this does not happen can anyone advise as to why?
Many Thanks in advance..
As far as my understanding of MVVM and your question goes, I have one main comment about your code.
I think your CustomImage is actually both Model and ViewModel layer, and you should split it in two :
the Model, which would contain the path itself ;
the ViewModel, which contain the BitmapImage and initialize it from the Model and constructing time.
The path is the mere data used for saving, and it fits the Model, whereas the BitmapImage is how the data is shown and should be constructed in the ViewModel.
One advantage is that now, your BitmapImage gets its own NotifyPropertyChanged call at setting time, and you won't have anymore problem or a View part directly bound to the Model.
As for your CustomImageViewModel, this looks like more of a MainViewModel-ish thing. You can still use this to store the ViewModels.
Related
I'm new to WPF so bear with me. I have a WinForms Application that I am trying to redo in WPF. In my current WinForms Application, I stick all my Controls into one Form, and hide/show them based on what buttons are hit, as well as making use of a second form.
My goal: Create different views to switch between smoothly based on what button is hit, instead of hiding Controls or making separate Forms and then hiding those.
I currently have a MainWindow view (My initial launch window), where with a button, I switch to my CreateAccount view. What I am having issues with is, how can I make my button in my CreateAccount go "back" to my MainWindow?
My end goal is to be able to switch between 4 views based off Button clicks.
Here is my MainWindow.xaml
<Window x:Class="MusicPlayer.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:MusicPlayer"
xmlns:Views="clr-namespace:MusicPlayer.Views"
xmlns:ViewModels="clr-namespace:MusicPlayer.ViewModels"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<DataTemplate x:Name="CreateAccountTemplate" DataType="{x:Type ViewModels:CreateAccountViewModel}">
<Views:CreateAccountView DataContext="{Binding}"/>
</DataTemplate>
</Window.Resources>
<Grid>
<Button x:Name="TestButton" Content="Button" HorizontalAlignment="Left" Margin="164,182,0,0" VerticalAlignment="Top" Height="61" Width="68" Click="CreateAccountView_Clicked"/>
<PasswordBox HorizontalAlignment="Left" Margin="164,284,0,0" VerticalAlignment="Top" Width="120"/>
<ContentPresenter Content="{Binding}"/>
</Grid>
</Window>
My MainWindow.xaml.cs
using System;
using System.Windows;
using MusicPlayer.ViewModels;
namespace MusicPlayer {
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
}
protected override void OnClosed(EventArgs e) {
base.OnClosed(e);
Application.Current.Shutdown();
} //end of onClosed
private void CreateAccountView_Clicked(object sender, RoutedEventArgs e) {
DataContext = new CreateAccountViewModel();
} //end of CreateAccountView_Clicked
}
}
And here is my CreateAccount.xaml
<UserControl x:Class="MusicPlayer.Views.CreateAccountView"
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:Views="clr-namespace:MusicPlayer.Views"
xmlns:ViewModels="clr-namespace:MusicPlayer.ViewModels"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
</UserControl.Resources>
<Grid Background="White">
<Button Content="Button" HorizontalAlignment="Left" Margin="276,279,0,0" VerticalAlignment="Top" Height="60" Width="59" Click="Button_Click"/>
</Grid>
</UserControl>
And my CreateAccountView.xaml.cs
using System.Windows;
using System.Windows.Controls;
using MusicPlayer.ViewModels;
namespace MusicPlayer.Views {
public partial class CreateAccountView : UserControl {
//public static readonly DependencyProperty TestMeDependency = DependencyProperty.Register("MyProperty", typeof(string), typeof(CreateAccountView));
public CreateAccountView() {
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e) {
DataContext = new MainWindowViewModel();
}
}
}
It seems to me that your current attempt is on the right track. The main issue with the code you posted is that the CreateAccountView.Button_Click() handler doesn't have access to the DataContext property it should be setting:
private void Button_Click(object sender, RoutedEventArgs e) {
DataContext = new MainWindowViewModel();
}
This DataContext property belongs to the CreateAccountView user control. However, this is not the controlling context for what's being displayed. So changing the value of that DataContext property doesn't have any useful effect. (Indeed, a user control should not set its own DataContext property at all, because doing so discards whatever context the client code using that user control had set.)
There's not enough context to know exactly what the best way for you to do this would be. I don't think it would be possible to provide enough context here on Stack Overflow. The overall architecture will depend on too many little details about your program. But, one way to approach this which I think is a good one would be this:
Create a "main" view model that governs the overall behavior of the app
Create individual view models that relate to different states of the UI
Have the main view model configure the individual view models to switch the current view model as appropriate, given the user input (e.g. clicking buttons)
Translating that into code, looks something like this…
First, the view models:
class MainViewModel : NotifyPropertyChangedBase
{
private object _currentViewModel;
public object CurrentViewModel
{
get => _currentViewModel;
set => _UpdateField(ref _currentViewModel, value);
}
private readonly HomeViewModel _homeViewModel;
private readonly Sub1ViewModel _sub1ViewModel;
private readonly Sub2ViewModel _sub2ViewModel;
public MainViewModel()
{
_sub1ViewModel = new Sub1ViewModel
{
BackCommand = new DelegateCommand(() => CurrentViewModel = _homeViewModel)
};
_sub2ViewModel = new Sub2ViewModel
{
BackCommand = new DelegateCommand(() => CurrentViewModel = _homeViewModel)
};
_homeViewModel = new HomeViewModel
{
ShowSub1Command = new DelegateCommand(() => CurrentViewModel = _sub1ViewModel),
ShowSub2Command = new DelegateCommand(() => CurrentViewModel = _sub2ViewModel)
};
CurrentViewModel = _homeViewModel;
}
}
class HomeViewModel : NotifyPropertyChangedBase
{
private ICommand _showSub1Command;
public ICommand ShowSub1Command
{
get => _showSub1Command;
set => _UpdateField(ref _showSub1Command, value);
}
private ICommand _showSub2Command;
public ICommand ShowSub2Command
{
get => _showSub2Command;
set => _UpdateField(ref _showSub2Command, value);
}
}
class Sub1ViewModel : NotifyPropertyChangedBase
{
private ICommand _backCommand;
public ICommand BackCommand
{
get => _backCommand;
set => _UpdateField(ref _backCommand, value);
}
}
class Sub2ViewModel : NotifyPropertyChangedBase
{
private ICommand _backCommand;
public ICommand BackCommand
{
get => _backCommand;
set => _UpdateField(ref _backCommand, value);
}
}
Of course, these view models contain only the implementation details needed to handle the UI switching. In your program, each would also include the stuff specific to each view state that you need.
In my little sample, the "home" view contains a couple of buttons, used to select the individual sub-views available:
<UserControl x:Class="WpfApp1.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"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Home: "/>
<Button Content="Sub1" Command="{Binding ShowSub1Command}"/>
<Button Content="Sub2" Command="{Binding ShowSub2Command}"/>
</StackPanel>
</UserControl>
The sub views just contain the button required to go back to the home view:
<UserControl x:Class="WpfApp1.Sub1View"
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="450" d:DesignWidth="800">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Sub1 View: "/>
<Button Content="Back" Command="{Binding BackCommand}"/>
</StackPanel>
</UserControl>
<UserControl x:Class="WpfApp1.Sub2View"
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="450" d:DesignWidth="800">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Sub2 View: "/>
<Button Content="Back" Command="{Binding BackCommand}"/>
</StackPanel>
</UserControl>
Finally, the main window sets the main view model, and declares templates to use for each of the specific sub views:
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:l="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<l:MainViewModel/>
</Window.DataContext>
<Window.Resources>
<DataTemplate DataType="{x:Type l:HomeViewModel}">
<l:HomeView/>
</DataTemplate>
<DataTemplate DataType="{x:Type l:Sub1ViewModel}">
<l:Sub1View/>
</DataTemplate>
<DataTemplate DataType="{x:Type l:Sub2ViewModel}">
<l:Sub2View/>
</DataTemplate>
</Window.Resources>
<StackPanel>
<ContentControl Content="{Binding CurrentViewModel}"/>
</StackPanel>
</Window>
Importantly, you'll see that none of the view objects include any code-behind. It's not necessary when you approach the problem this way, at least not for the purpose of controlling basic behaviors in the code. (You may still wind up with code-behind for view objects, but this will usually only be for the purpose of implementing specific user-interface behaviors unique to that view object, not for dealing with the view model state.)
Using this approach, you let WPF do as much of the heavy-lifting as possible. It also decouples all of the view model objects from each other. There's a clear hierarchy: only the top-level "main" view model even knows about the other view models. This allows the sub-view models ("home", "sub1", and "sub2") to be reused as necessary in other scenarios without any modification or special-case handling within them.
Here are the helper classes I used above:
class NotifyPropertyChangedBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void _UpdateField<T>(ref T field, T newValue,
Action<T> onChangedCallback = null,
[CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, newValue))
{
return;
}
T oldValue = field;
field = newValue;
onChangedCallback?.Invoke(oldValue);
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
class DelegateCommand : ICommand
{
private readonly Action _execute;
public DelegateCommand(Action execute)
{
_execute = execute;
}
#pragma warning disable 67
public event EventHandler CanExecuteChanged;
#pragma warning restore
public bool CanExecute(object parameter) => true;
public void Execute(object parameter) => _execute();
}
I have a WPF MVVM project which has a property of type OudPattern.
I would like to bind this property to OudPatternEditor1 which is a UserControl in view. is there any way to do this using INotifyPropertyChange. Since I'm already using it in my project.
Please notice that OudPatternEditor1 is instantiated at the startup using a new OudPattern(). But this pattern can be changed by user. so I need to update this property in the view model and OudEditor1 also.
I solved the problem by passed the view to the view model and updating this property in there each time. But I know that this approach is against the MVVM principles of decoupling view and view model.
I also tried to solve this using dependency property but i failed to get a solution.
Custom user control XAML
<UserControl x:Class="MyOudTeacher.OudMachine.OudPatternEditor"
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" Visibility="Visible">
<Canvas x:Name="oudGridCanvas"/>
</UserControl>
Code-behind
public partial class OudPatternEditor : UserControl
{
private OudPattern oudPattern;
private double gridSquareWidth = 15;// my code: default 20
private double namesColumnWidth = 50;//my code: default 100
public OudPatternEditor()
{
InitializeComponent();
this.oudPattern = new OudPattern();
DrawNoteNames();
DrawPattern(namesColumnWidth);
DrawGridLines(namesColumnWidth);
}
public OudPattern OudPattern
{
get { return oudPattern; }
set {
oudPattern = value;
UpdateHitView();
}
}
/* more code... */
}
The view XAML
<UserControl x:Class="MyOudTeacher.OudMachine.OudMachineView"
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="500" xmlns:my="clr-namespace:MyOudTeacher.OudMachine">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="262*" />
</Grid.RowDefinitions>
<my:OudPatternEditor Grid.Row="1" HorizontalAlignment="Left" Margin="20" x:Name="oudPatternEditor1" VerticalAlignment="Top"/>
<StackPanel Orientation="Horizontal">
<Button Command="{Binding StopCommand}" Margin="2" ToolTip="Stop">
<Rectangle Fill="DarkBlue" Width="15" Height="15" Margin="3" RadiusX="2" RadiusY="2"/>
</Button>
<!-- more code... -->
view model
class OudMachineViewModel : ViewModelBase, IDisposable
{
private IWavePlayer waveOut;
private OudPattern pattern;
private OudPatternEditor OudPatternEditor;
private OudPatternSampleProvider patternSequencer;
private int tempo;
private string selectedFile;
public ICommand PlayCommand { get; }
public ICommand StopCommand { get; }
public ICommand UpdateXmlCommand { get; }
public ICommand OpenFileCommand { get; }
public ICommand PauseCommand { get; }
public OudMachineViewModel(OudPatternEditor oudPatternEditor)
{
this.OudPattern = oudPatternEditor.OudPattern;
this.OudPatternEditor = oudPatternEditor;
Tempo = OudPattern.ScoreHits.ScaledTempo;
PlayCommand = new DelegateCommand(Play);
StopCommand = new DelegateCommand(Stop);
OpenFileCommand = new DelegateCommand(OpenFile);
PauseCommand = new DelegateCommand(Pause);
}
public OudPattern OudPattern
{
get { return pattern; }
set
{
pattern = value;
OnPropertyChanged("OudPattern");
}
}
private void UpdateSelectedFile()
{
OudPattern = new OudPattern(selectedFile);
OudPatternEditor.OudPattern = this.OudPattern;
Tempo = OudPattern.ScoreHits.ScaledTempo;
}
/* more code... */
The correct way is NOT to pass OudEditor to the view model constructor. since this is against MVVM. I expect to find a way to update OudEditor1.OudPattern by binding it to ViewModel.OudPattern
Edit #1:
I was able to add a dependency property and achieve what I wanted. The code added is simple:
view code-behind:
public OudPattern OudPattern
{
get { return (OudPattern)GetValue(OudPatternProperty); }
set
{
SetValue(OudPatternProperty, value);
DrawNewPattern();
UpdateHitView();
}
}
public static readonly DependencyProperty OudPatternProperty =
DependencyProperty.Register("OudPattern", typeof(OudPattern), typeof(OudPatternEditor),
new PropertyMetadata(default(OudPattern)));
view XAML above, modified:
<my:OudPatternEditor Grid.Row="1" HorizontalAlignment="Left" Margin="20" x:Name="oudPatternEditor1" VerticalAlignment="Top" OudPattern="{Binding OudPattern}"/>
view model:
public OudPattern OudPattern
{
get { return pattern; }
set
{
pattern = value;
OnPropertyChanged("OudPattern");
}
}
But I noticed that the setter for the dependency property in my view code-behind never executes. I learned that those setters are not executed when called from outside. How do I make the view code-behind react to external change and do the methods mentioned in the setter above? i.e.
DrawNewPattern();
UpdateHitView();
Edit# 2:
I found an answer to my last question about dependency property setter. Thanks for Elgonzo to point out that I can use property changed callbacks.
The code should be modified to add OnPatternChanged callback
public static readonly DependencyProperty OudPatternProperty =
DependencyProperty.Register("OudPattern", typeof(OudPattern), typeof(OudPatternEditor),
new PropertyMetadata(default(OudPattern),OnPatternChanged));
private static void OnPatternChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var myObject = (OudPatternEditor)d;
myObject.DrawNewPattern();
myObject.UpdateHitView();
Note that you cant call none static methods directly from static OnPatternChanged.. so you need to cast the sender into object and call its member methos.
I have been a few hours now trying to understand how to do data-binding.
Initially I was following some examples but they all show to do the databinding using {Binding Source={StaticResource myObject}, Path=myObject.myProperty}
or {Binding Path=myObject.myProperty}
Nothing of this seem to bind the Config object inside the controller that is inside the Window.
If I do the binding as an StaticResource it does the binding to an object of the Controller class but is NOT the object that is created inside the window class, this Config seems to be a new separate instance. This is the part I don't understand. If someone could explain or give me some reference where to look I would greatly appreciate it.
This is some code very simplified
Window1.cs
<Window x:Class="Sample.UI.Main"
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:controller="clr-namespace:Sample.Controller"
mc:Ignorable="d"
Title="SampleApp" Height="600" Width="800" ResizeMode="NoResize" WindowStartupLocation="CenterScreen">
<Window.Resources>
<ResourceDictionary>
<controller:PublisherController x:Key="oController" />
</ResourceDictionary>
</Window.Resources>
<CheckBox x:Name="chkBoxShowRoom" Style="{StaticResource checkBoxTemplate}" Content="{StaticResource configShowRoom}" IsChecked="{Binding Source={StaticResource oController}, Path=Config.ShowRoom}"/>
Then my Window1.cs
public partial class Main : Window
{
public PublisherController Controller { get; set; }
Then Controller.cs
public class PublisherController
{
public Configuration Config { get; set; }
Then the Configuration.cs
public class Configuration : AbstractEntity, INotifyPropertyChanged
{
private bool _ShowRoom;
public bool ShowRoom
{
get
{
return _ShowRoom;
}
set
{
if (value != _ShowRoom)
{
this._ShowRoom = value;
OnPropertyChanged();
}
}
}
...
I have a textblock in WPF which is bound to a property in my ViewModel class. On click of a button I wish to modify the property and expect the same to be reflected in my textblock. I want all these to be done purely using MVVM (MVVMLight). I am using MMVM light and VS 2012.
Challenges- On button click the changes are not being reflected. Though the program execution is going inside the property , changes are not being made.
Please Help !!
Program- View:
<Window x:Class="MvvmLight1_Trail.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"
mc:Ignorable="d ignore"
Height="500"
Width="500"
Title="MVVM Light Application"
DataContext="{Binding Main, Source={StaticResource Locator}}">
<Grid x:Name="LayoutRoot">
<TextBlock FontSize="34"
Text="{Binding Path=MyText,UpdateSourceTrigger=Default, Mode=TwoWay}"
VerticalAlignment="Center"
HorizontalAlignment="Center"
TextWrapping="Wrap" />
<Button Width="100" Height="100" Command="{Binding PressCommand}" Margin="198.985,277.537,193.014,92.462" Content="Press Me"/>
</Grid>
View Model
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using MvvmLight1_Trail.Model;
using System.ComponentModel;
using System.Threading;
namespace MvvmLight1_Trail.ViewModel
{
public class MainViewModel : ViewModelBase
{
public RelayCommand PressCommand { get; private set; }
Thread t;
private string _welcomeTitle = string.Empty;
public string MyText
{
get
{
return _welcomeTitle;
}
set
{
if (_welcomeTitle == value)
{
return;
}
_welcomeTitle = value;
RaisePropertyChanged(MyText);
}
}
/// <summary>
/// Initializes a new instance of the MainViewModel class.
/// </summary>
public MainViewModel()
{
PressCommand = new RelayCommand(() => MyFunc());
myfunc();
}
private void MyFunc()
{
this.MyText = "Hi2";
}
private void myfunc()
{
this.MyText = "Hello";
this.MyText = "Hi";
}
}
}
Replace
RaisePropertyChanged(MyText);
to
RaisePropertyChanged("MyText");
PropertyChanged event should be raised on property name and not on property value.
Already answered by #Rohit Vats. You can also call RaisePropertyChanged like, RaisePropertyChanged( () => MyText) to ease renaming later.
Late to the game but:
in new C# 6 you can also use nameof like this:
RaisePropertyChanged(nameof(MyText))
I set my DataGrid to collapse when there are no items
<DataGrid Name="dataGrid"
Visibility="{Binding HasItems,
ElementName=dataGrid,
Converter={StaticResource BooleanToVisibilityConverter}}">
</DataGrid>
The problem is, I would like it to appear on design mode. How to do it? Should I create fake data?
I tried
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
if (DesignerProperties.GetIsInDesignMode(this))
{
this.dataGrid.ItemsSource = new List<Table> { new Table() };
}
}
but it didn't work
If I understand what you are trying to do, this works well for me:
<Window x:Class="WPFScratch.MainWindow"
xmlns:local="clr-namespace:WPFScratch"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="592"
d:DataContext="{d:DesignInstance local:MyDesignTimeViewModel, IsDesignTimeCreatable=True}"
Title="MainWindow" SizeToContent="WidthAndHeight">
<Window.Resources>
<local:BoolToVisibilityConverter
x:Key="BoolToHiddenConverter"
TrueValue="Visible" FalseValue="Hidden" />
</Window.Resources>
<DockPanel>
<DataGrid Name="dataGrid" ItemsSource="{Binding People}"
Visibility="{Binding HasItems,
ElementName=dataGrid,
Converter={StaticResource BoolToHiddenConverter}}" AutoGenerateColumns="True">
</DataGrid>
</DockPanel>
public class MyDesignTimeViewModel
{
public ObservableCollection<Person> People
{
get
{
return new ObservableCollection<Person> {
new Person
{
Name = "Simon"
},
new Person
{
Name = "Jack"
}
};
}
}
}
public class Person
{
public string Name { get; set; }
}
I had a similar problem in a Silverlight app where I did not want to show any content to the user until they were logged in. I set the visibility of the View to "Collapsed" in the constructor of the View, and then back to "Visible" after the user was Authenticated/Authorized. I suggest that you bind to the Visibility property of your Grid in the constructor of the View so that this doesn't take effect until the code executes, thereby letting you see the Grid in design view. I haven't done much in WPF, but something like this might work:
Binding b = new Binding("Visibility");
b.Source = dataGrid.HasItems;
b.Converter = new BooleanToVisibilityConverter();
BindingOperations.SetBinding(dataGrid, VisibilityProperty, b);
Again, I have no idea if this will work in WPF, but maybe this will get you closer.