I was just watching this video on XAML where he created a clock and noticed he could actually see all the changes he is doing from C Sharp in the Xaml design view.
At 33:30 he creates his class: https://youtu.be/Wb-l0e6WYE0?t=2008
At 37:10 he binds to that class: https://youtu.be/Wb-l0e6WYE0?t=2227
Later on at 40:17 you can actually see the clock is ticking in the design view!
I tried to do this by creating a class called Ball with some properties like size and bind those properties to a Canvas with a rectangle that has a EllipseGeometry clip which makes it round. It works fine when running the application but the design view is just white.
Does anyone know how he does this?
My Code:
MainWindow.xaml
<Window x:Class="XamlTest.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:XamlTest"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Canvas Background="White">
<Rectangle Height="{Binding Size}" Width="{Binding Size}" Fill="Green" Canvas.Top="40">
<Rectangle.Clip>
<EllipseGeometry Center="{Binding EllipseCenter}" RadiusX="{Binding EllipseRadius}" RadiusY="{Binding EllipseRadius}"/>
</Rectangle.Clip>
</Rectangle>
<Button x:Name="button" Content="Button" Width="75" Click="button_Click"/>
</Canvas>
Code Behind:
using System.Windows;
namespace XamlTest
{
public partial class MainWindow : Window
{
Ball TheBall = new Ball();
public MainWindow()
{
InitializeComponent();
TheBall.Size = 300;
this.DataContext = TheBall;
}
private void button_Click(object sender, RoutedEventArgs e)
{
TheBall.Size = TheBall.Size + 40;
}
}
}
Ball Class:
using System.Windows;
namespace XamlTest
{
class Ball : INotifyPropertyChangedBase
{
public Ball()
{
Size = 50;
}
private double _size;
public double Size
{
get
{
return _size;
}
set
{
_size = value;
EllipseCenter = new Point(_size / 2, _size / 2);
EllipseRadius = _size / 2;
OnPropertyChanged("Size");
}
}
private Point _ellipseCenter;
public Point EllipseCenter
{
get
{
return _ellipseCenter;
}
set
{
_ellipseCenter = value;
OnPropertyChanged("EllipseCenter");
}
}
private double _ellipseRadius;
public double EllipseRadius
{
get {
return _ellipseRadius;
}
set {
_ellipseRadius = value;
OnPropertyChanged("EllipseRadius");
}
}
}
}
INotifyPropertyChangedBase Class:
using System.ComponentModel;
namespace XamlTest
{
public class INotifyPropertyChangedBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
internal void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
I also have a button that increase the size of the ball!
Thanks for the help.
The DataContext allows the XAML to find an instance the class that it is meant to be binding to.
Then, the bindings in XAML allow you to bind to specific properties of said class.
There are two separate DataContexts: design time and run time.
To set the design time DataContext, see:
http://adamprescott.net/2012/09/12/design-time-data-binding-in-wpf/
Essentially, when you set the design time DataContext, behind the scenes the WPF runtime will automatically instantiate a new instance of the class you point it at (or simply point at the class if its static), and then the Visual Studio design time designer will display live values from your class, as you are editing the XAML. This makes designing really fast, as you can work with live data, and you dont have to run the program all the time to see how it looks.
To set the run time DataContext, see ReSharper WPF error: "Cannot resolve symbol "MyVariable" due to unknown DataContext". The answer describes how to use the free Snoop utility to detect runtime binding errors.
Added this code:
d:DataContext="{d:DesignInstance local:Ball,IsDesignTimeCreatable=True}"
And now I can see my green ball at design time!
Thanks!
Related
I am coding an application, its a quiz, I have a main Window where I load different UserControls (Pages). so my problem is that I have one image on the MainWindow, I want to change the Visibility of this image from Collapsed to Visible from one of the UserControls but with no luck...
Here is my MainWindow:
<Window x:Class="MuseonQuiz_v3.PageSwitcher"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:pages="clr-namespace:MuseonQuiz_v3.Pages"
xmlns:k="http://schemas.microsoft.com/kinect/2013"
Title="MainWindow" Height="710" Width="1127" IsEnabled="True" DataContext="{Binding}" FontFamily="KaiTi" ResizeMode="NoResize" WindowStyle="None"
WindowStartupLocation="CenterScreen" WindowState="Maximized">
<Grid>
<Grid>
<k:KinectRegion Name="kinectRegion">
<ContentControl x:Name="mainContentControl"/>
</k:KinectRegion>
</Grid>
<Grid>
<Grid.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVisConverter" />
</Grid.Resources>
<k:KinectSensorChooserUI HorizontalAlignment="Center" VerticalAlignment="Top" Name="sensorChooserUi" />
<k:KinectUserViewer VerticalAlignment="Bottom" HorizontalAlignment="Center" k:KinectRegion.KinectRegion="{Binding ElementName=kinectRegion}" Height="600" Width="600" />
<Image Name="colorStreamImage" Width="640" Height="480" Visibility="Collapsed" HorizontalAlignment="Center" />
</Grid>
</Grid>
and this is my UserControl:
public partial class Selectie : UserControl, ISwitchable
{
string backgroundSelectie = "pack://application:,,,/MuseonQuiz_v3;component/Images/Selectie/selectie_background.jpg";
public Selectie()
{
InitializeComponent();
selectieBackground();
animatieButtons();
}
#region ISwitchable Members
public void UtilizeState(object state)
{
throw new NotImplementedException();
}
#endregion
}
My question is... how do I change the Visibility of the colorStreamImage that is located in the MainWindow from the UserControl... I have tried making an instance of the MainWindow, but that does not work, maybe I have to use some binding, but I am not sure, I appreciate any help you can provide!
As Clemens mentioned, your best bet is to go down the MVVM path. This is a good tutorial to get started In the Box – MVVM Training.
First, you can create a view model that implements INotifyPropertyChanged. In this case, you may want it to have at least one property of type Visibility.
public class MainViewModel : INotifyPropertyChanged
{
private Visibility _imageVisibility;
public Visibility ImageVisibility
{
get { return _imageVisibility; }
set { _imageVisibility = value; OnPropertyChanged("ImageVisibility"); }
}
private BitmapImage _imageSource;
public BitmapImage ImageSource{...}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler eventHandler = PropertyChanged;
if (eventHandler != null)
eventHandler(this, new PropertyChangedEventArgs(propertyName));
}
}
Now you'll want to set this view model as the data context on the main window. To do this, Paul Stovell has a good post on the different approaches: http://paulstovell.com/blog/mvvm-instantiation-approaches. Once we set it on the main window, the Selectie element will inherit the data context. Using the simplest approach:
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainViewModel();
}
Your Image element might then bind to the property like this:
<Image Visibility="{Binding ImageVisibility, UpdateSourceTrigger=PropertyChanged}" Source="{Binding ImageSource}" Height="200" Width="200"></Image>
The Selectie element can now change the ImageVisbility property on the view model since it shares the same data context as MainWindow. (I used the code-behind as an example. You'll probably want to push as much of that logic out of the view and into the view model or further downstream)
public partial class Selectie : UserControl
{
public Selectie()
{
InitializeComponent();
}
private void Selectie_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
if (((MainViewModel)this.DataContext).ImageVisibility == System.Windows.Visibility.Visible)
((MainViewModel)this.DataContext).ImageVisibility = System.Windows.Visibility.Collapsed;
else
((MainViewModel)this.DataContext).ImageVisibility = System.Windows.Visibility.Visible;
}
}
I have a user control "CtrlComments", this control has the following XAML (It's super basic).
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:wpftoolkit="http://schemas.microsoft.com/wpf/2008/toolkit"
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"
x:Name="ucRoot">
<Grid>
<StackPanel Orientation="Horizontal">
<TextBlock Text="ID: " />
<TextBlock Text="{Binding Path=Deployment.Id}" />
</StackPanel>
</Grid>
The code behind is as follows, it's the bare basics to get the control to function. The key is the DependencyObject typeof(DeploymentDto) which has an int property called Id that we are interested in showing on our window as per XAML binding above.
public partial class CtrlComments : UserControl, INotifyPropertyChanged
{
public static readonly DependencyProperty DeploymentProperty =
DependencyProperty.Register("Deployment", typeof(DeploymentDto),
typeof(CtrlComments), new PropertyMetadata(new DeploymentDto()));
public DeploymentDto Deployment
{
get
{
return (DeploymentDto)GetValue(DeploymentProperty);
}
set
{
SetValue(DeploymentProperty, value);
OnPropertyChanged(new PropertyChangedEventArgs("Deployment"));
}
}
public CtrlComments()
{
InitializeComponent();
this.DataContext = this;
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
PropertyChanged(this, e);
}
}
Our problem is, despite the fact that the binding between the parent control and my user control via the dependency property is working (verified) and the OnPropertyChanged method firing, the TextBlock in my XAML isn't updating.
I have noticed that when the OnPropertyChanged method is run, the eventhandler is null meaning no one is notified that there was a property change.
I don't understand why this is the case though. If you could help explain where we are going wrong it would be enormously appreciated.
Thanks!
I have tried to replicate your problem and while doing so, I figured that the problem for me was in the following line in CtrlComments:
this.DataContext = this;
Dropping this line just made it work for me. Also note (as #Aron wrote in the comments) that the OnPropertyChanged of INotifyPropertyChanged shouldn't be called while in the setter of the DependencyProperty. At least for me it isn't necessary to implement INPC at all.
In the XAML file where you are using the UserControl you are most likely going to have another DataContext set (on a higher level, perhaps in the Window), and thus I guess it isn't inherited to the user control if already set in there (or overwritten). Below is my working code, but perhaps I misunderstood exactly what you're doing. If that is the case, please extend your question to include how you are using the UserControl, as that is a key to answering the question if this doesn't work :)
CtrlComments.xaml:
<UserControl x:Class="WpfApplication1.CtrlComments"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<StackPanel Orientation="Horizontal">
<TextBlock Text="ID: "/>
<TextBlock Text="{Binding Path=Deployment.Id}"/>
</StackPanel>
</Grid>
</UserControl>
CtrlComments.xaml.cs:
namespace WpfApplication1
{
public partial class CtrlComments : UserControl
{
public static readonly DependencyProperty DeploymentProperty =
DependencyProperty.Register("Deployment", typeof(DeploymentDto), typeof(CtrlComments), new PropertyMetadata(new DeploymentDto { Id = 5 }));
public DeploymentDto Deployment
{
get { return (DeploymentDto)GetValue(DeploymentProperty); }
set
{
SetValue(DeploymentProperty, value);
}
}
public CtrlComments()
{
InitializeComponent();
}
}
}
MainWindow.xaml:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
xmlns:local="clr-namespace:WpfApplication1"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<StackPanel>
<local:CtrlComments x:Name="testUC" Height="100" Deployment="{Binding Deployment}"/>
<Button Click="Button_Click" Height="50" Width="100"/>
</StackPanel>
</Window>
MainWindow.xaml.cs:
namespace WpfApplication1
{
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
}
private DeploymentDto deployment = new DeploymentDto { Id = 2 };
public DeploymentDto Deployment
{
get { return deployment; }
set { deployment = value; OnPropertyChanged("Deployment"); }
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Deployment = new DeploymentDto { Id = new Random().Next(100) };
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
}
DeploymentDto:
public class DeploymentDto
{
public int Id { get; set; }
}
It's quite ugly to bind MainWindow.DataContext to its code-behind, but since it's just used for example purposes I hope it's okay :)
Im trying to create a list of classes for a degree plan at my university where when classes that have been taken are checked another class gets highlighted to let the user know that class has all of the prerequisites met to be taken. so if i check calculus 1, physics 1 will get highlighted.
Im new to C# and i dont have a heavy knowledge of what the language and .NET framework can do so im asking for a good simple straight answer, if you could explain exactly what is going on in the code that would be fantastic. Thanks
heres what i have so far. just a basic proof of concept WPF
<Window x:Class="degree_plan.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Degree Planner" Height="350" Width="525" Name="Degree">
<Grid>
<CheckBox Content="Math 1412" Height="16" HorizontalAlignment="Left" Margin="34,40,0,0" Name="checkBox1" VerticalAlignment="Top" />
<CheckBox Content="Physics 1911" Height="16" HorizontalAlignment="Left" Margin="34,62,0,0" Name="checkBox2" VerticalAlignment="Top" />
</Grid>
</Window>
and heres the c# code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
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 degree_plan
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
// if (checkBox1.Checked)
// Console.WriteLine("physics 1");
}
}
}
You can register an event handler for the checkboxes:
AddHandler(CheckBox.CheckedEvent, new RoutedEventHandler(CheckBox_Click));
then, create the event handler:
private void CheckBox_Click(object sender, RoutedEventArgs e)
{
CheckBox checkbox = e.Source as CheckBox
//...
}
The checkbox variable in the event handler is the checkbox which was clicked to raise the event. You can check which checkbox it was, and then enable all the options which depend on it.
I know you asked for simple but at some point you can come back to this as it's a very structured and expandable way to hold and use data in WPF
I would consider quantifying the Classes in their own structure, each with a list of the prerequisite classes that must be completed before hand, I would like to suggest using the following to achieve what you're after (sorry, bit long!)
what you'll get is a list of classes represented by checkboxes, you can only check a class once all of its prerequisite classes are complete, they have names and descriptions and can be customised on the UI in anyway you want.
Create a new WPF application and add the following Class.
Class.cs
public class Class : Control, INotifyPropertyChanged
{
// Property that's raised to let other clases know when a property has changed.
public event PropertyChangedEventHandler PropertyChanged;
// Flags to show what's going on with this class.
bool isClassComplete;
bool isPreComplete;
// Some other info about the class.
public string ClassName { get; set; }
public string Description { get; set; }
// A list of prerequisite classes to this one.
List<Class> prerequisites;
// public property access to the complete class, you can only set it to true
// if the prerequisite classes are all complete.
public bool IsClassComplete
{
get { return isClassComplete; }
set
{
if (isPreComplete)
isClassComplete = value;
else
if (value)
throw new Exception("Class can't be complete, pre isn't complete");
else
isClassComplete = value;
PropertyChangedEventHandler temp = PropertyChanged;
if (temp != null)
temp(this, new PropertyChangedEventArgs("IsClassComplete"));
}
}
// public readonly property access to the complete flag.
public bool IsPreComplete { get { return isPreComplete; } }
public Class()
{
prerequisites = new List<Class>();
isPreComplete = true;
}
// adds a class to the prerequisites list.
public void AddPre(Class preClass)
{
prerequisites.Add(preClass);
preClass.PropertyChanged += new PropertyChangedEventHandler(preClass_PropertyChanged);
ValidatePre();
}
// removes a class from the prerequisites lists.
public void RemovePre(Class preClass)
{
prerequisites.Remove(preClass);
preClass.PropertyChanged -= new PropertyChangedEventHandler(preClass_PropertyChanged);
ValidatePre();
}
// each time a property changes on one of the prerequisite classes this is run.
void preClass_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case "IsClassComplete":
// check to see if all prerequisite classes are complete/
ValidatePre();
break;
}
}
void ValidatePre()
{
if (prerequisites.Count > 0)
{
bool prerequisitesComplete = true;
for (int i = 0; i < prerequisites.Count; i++)
prerequisitesComplete &= prerequisites[i].isClassComplete;
isPreComplete = prerequisitesComplete;
if (!isPreComplete)
IsClassComplete = false;
}
else
isPreComplete = true;
PropertyChangedEventHandler temp = PropertyChanged;
if (temp != null)
temp(this, new PropertyChangedEventArgs("IsPreComplete"));
}
}
Now in the code behind for MainWindow.cs you can create a collection of classes, I've done this in the constructor and provided an observable collection of classes so when new classes are added, you don't have to do anything to get them to display on the UI
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public ObservableCollection<Class> Classes
{
get { return (ObservableCollection<Class>)GetValue(ClassesProperty); }
set { SetValue(ClassesProperty, value); }
}
public static readonly DependencyProperty ClassesProperty = DependencyProperty.Register("Classes", typeof(ObservableCollection<Class>), typeof(MainWindow), new UIPropertyMetadata(null));
public MainWindow()
{
InitializeComponent();
Class math = new Class()
{
ClassName = "Math 1412",
Description = ""
};
Class physics = new Class()
{
ClassName = "Physics 1911",
Description = "Everything everywhere anywhen",
};
physics.AddPre(math);
Classes = new ObservableCollection<Class>();
Classes.Add(math);
Classes.Add(physics);
}
}
The final step is to tell WPF what a class is supposed to look like on the user interface, this is done in resources, to simplify the example, I've put it in the MainWindow.xaml file.
<Window x:Class="WpfApplication8.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication8"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<!-- This tells WPF what a class looks like -->
<Style TargetType="{x:Type local:Class}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:Class}">
<StackPanel Orientation="Horizontal" DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}">
<!-- Checkbox and a text label. -->
<CheckBox IsEnabled="{Binding IsPreComplete}" IsChecked="{Binding IsClassComplete}" />
<TextBlock Margin="5,0,0,0" Text="{Binding ClassName}" ToolTip="{Binding Description}"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<!-- This draws the list of classes from the property in MainWindow.cs-->
<ItemsControl ItemsSource="{Binding Classes}"/>
</Grid>
</Window>
For your ease,
try to use the 'CheckedChanged' event.. Just double click the CheckBox on the designer. Handler will be added automatically(something like,
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
}
.Then add your code there. But, this is time consuming(since, you have to add handler for each CheckBox). But, 'll be easy for you to understand at this stage.
I'm very new to WPF and a beginner in C#.NET. I'm currently making an application where there will be many pages and the trigger to change the page is hand gesture using Kinect SDK (the trigger method is not relevant for this question). Normally when a WPF file is created, there will be a similarly named .cs file attached to it, which acts somewhat like a controller. However, I need multiple WPF files/pages to be controlled only by a single controller .cs file. How do I achieve that? Thanks for viewing my question and your answer will be very appreciated :)
You probably want to write a class that contains your 'controller' code and reference it from your WPF UserControls / Pages.
In a new file:
public class MyController
{
public void DoThings(object parameter)
{
// stuff you want to do
}
}
and then inside your UserControl code-behind class:
public partial class MyWpfControl : UserControl
{
private MyController controller;
public MyWpfControl
{
this.controller = new MyController();
}
}
and finally, tie your events back to the controller's method:
private void OnGesture(object sender, EventArgs e)
{
// call the method on the controller, and pass whatever parameters you need...
this.controller.DoThings(e);
}
The code behind is really part of the view and isn't really analogous to a controller and generally there shouldn't be much code in them. Typically you would want most of your logic between your "View Model" which serves as an abstraction of the view and "Model" which serves as an abstraction of the business logic that your UI is interacting with.
In this light what I think you really want is a View Model(VM) that controls multiple views. This is a fairly typical scenario and the preferred method (IMO anyway) is to have a hierarchical view model that has a top level the application model and a number of sub VMs that represent different components within your UI, though you can bind everything to your top level VM if you really want to.
To do this we would first define our view model like so
public interface IGestureSink
{
void DoGesture();
}
public class MyControlVM : INotifyPropertyChanged, IGestureSink
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };
private ApplicationVM parent;
public MyControlVM(ApplicationVM parent)
{
this.Name = "my user control";
this.parent = parent;
parent.PropertyChanged += (s, o) => PropertyChanged(this, new PropertyChangedEventArgs("Visible"));
}
public String Name { get; set; }
public bool Visible { get { return parent.ControlVisible; } }
public void DoGesture()
{
parent.DoGesture();
}
}
public class ApplicationVM : INotifyPropertyChanged, IGestureSink
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public ApplicationVM()
{
this.ControlVM = new MyControlVM(this);
this.ControlVisible = false;
}
public MyControlVM ControlVM { get; private set; }
public bool ControlVisible {get; set;}
public void DoGesture()
{
this.ControlVisible = !this.ControlVisible;
PropertyChanged(this, new PropertyChangedEventArgs("ControlVisible"));
}
}
and then all we need to do is to build a user control
<UserControl x:Class="WpfApplication2.MyControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid Background="LightBlue">
<Label Content="{Binding Name}"/>
</Grid>
</UserControl>
and page
<Window xmlns:my="clr-namespace:WpfApplication2" x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</Window.Resources>
<Grid>
<my:MyControl Width="200" Height="200" x:Name="myUserControl" DataContext="{Binding ControlVM}" Visibility="{Binding Visible,Converter={StaticResource BooleanToVisibilityConverter}}"/>
<Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="222,262,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" />
</Grid>
</Window>
That use it. The only thing that we need in our code behind is a constructor that sets up the page VM and wiring from our button to the view model.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new ApplicationVM();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
((IGestureSink)(this.DataContext)).DoGesture();
}
}
If you wanted to use a monolithic view model instead you would use this Instead of binding the DataContext to ControlVM:
<my:MyControl Width="200" Height="200" x:Name="myUserControl" DataContext="{Binding DataContext}" Visibility="{Binding ControlVisible,Converter={StaticResource BooleanToVisibilityConverter}}"/>
I'm doing a very simple implementation of the MVC pattern in C# with a WPF interface.
I have a model that's keeping the state. I want to be able to notify the view form the model whenever anything about the state changes, so that the view can update itself accordingly.
What's the simplest best practice for doing this in WPF? I know there's such a thing as a PropertyChanged event, is that what I'm looking for or is that too specific for my situation?
Thanks!
Yes. Implement the interface INotifyPropertyChanged.
An example:
MainWindow.xaml
<Window x:Class="INotifyChangedDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Label Content="{Binding HitCount}"></Label>
<Button Grid.Row="1" Click="Button_Click">
Hit
</Button>
</Grid>
MainWindow.xaml.cs
namespace INotifyChangedDemo
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private MainViewModel _viewModel = new MainViewModel();
public MainWindow()
{
InitializeComponent();
DataContext = _viewModel;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
_viewModel.HitCount = _viewModel.HitCount + 1;
}
}
}
MainViewModel.cs
namespace INotifyChangedDemo
{
public class MainViewModel : INotifyPropertyChanged
{
private int _hitCount;
public int HitCount
{
get
{
return _hitCount;
}
set
{
if (_hitCount == value)
return;
_hitCount = value;
// Notify the listeners that Time property has been changed
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("HitCount"));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
For better implementation of INotifyChangedProperty, please refer to this thread: Automatically INotifyPropertyChanged.
If you wanna know more about the MVVM pattern, please see here: http://msdn.microsoft.com/en-us/magazine/dd419663.aspx
If your view binds to a property declared in your model, and your property raises the PropertyChanged event whenever it is changed, then your view will automatically be updated with the new value. For instance, your view might declare the following:
<TextBlock Text="{Binding Name}" />
And in your model you would have:
string _name;
public string Name
{
get
{
return _name;
}
set
{
_name = value;
RaisePropertyChanged("Name");
}
}
This assumes that you are using some framework / helper that provides the RaisePropertyChanged method. I am taking this example from the Galasoft MVVM framework, but I assume that exactly the same principal applies in MVC.
Hope this helps.