I am quite proficient in WPF but new to the area of implementing UI validation where I need to ensure that certain values are ready for saving to a database. In my very large application I have a lot of different types of validation, which includes the simple single (TextBox requires a value or minimum characters), (Item must be selected), (At least one option must be chosen) and so on.
I have implemented the validation using INotifyDataErrorInfo and this works really well, except for a few issues that I am going around in circles with and need some guidance. In fact, this is probably more of styling question. The following is just one of my issues but if I solve this then it may solve the others so I’ll stick to this for now:
I have a set of radio button controls where one must be selected by the user but I do not want any to be selected by default, so are forced to make the choice. Until they choose one, a red border needs to be displayed around the radio buttons which are in a stack panel. Because this is something that I want to do in several places where I have a group of controls, I thought it would be good to create a Border control called ErrorBorderControl, that manages data errors using a property and then pop the items into this control. I create a DependecyProperty on this control called ValidationObject of type object, that just takes a property that can be tested to see if there is an error. This works perfectly and the red border is displayed when not selected and not when selected. Great so far. However, the ControlTemplate defined in the ErrorBorderControl bleeds to all other Border based controls in the UI, including the Border around the RadioButton controls.
I work a lot with styles and understand scope but this is very odd. The below is what I have done, although very basic as a first attempt:
User Control:
<UserControl
x:Class="Itec.Common.Wpf.CustomControls.Controls.ErrorBorderControl"
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">
<!--
Provides a border around grouped controls that needs error feedback
-->
<Border>
<!-- Style -->
<Border.Style>
<Style
TargetType="{x:Type Border}"
x:Name="TheTemplate">
<!-- Error template definition -->
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<!-- Adorner for the error visual -->
<AdornedElementPlaceholder>
<!-- Simple border around the control -->
<Border
BorderBrush="#ec7063"
BorderThickness="1"/>
</AdornedElementPlaceholder>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Border.Style>
</Border>
</UserControl>
Code Behind:
/// <summary>
/// Interaction logic for ErrorBorderControl.xaml
/// </summary>
public partial class ErrorBorderControl : UserControl
{
#region Dependency Properties
/// <summary>
/// The validation object property
/// </summary>
public static readonly DependencyProperty ValidationObjectProperty =
DependencyProperty.Register("ValidationObject", typeof(object), typeof(ErrorBorderControl),
new FrameworkPropertyMetadata(OnValidationObjectChanged));
#endregion Dependency Properties
#region Ctors
/// <summary>
/// Initializes a new instance of the <see cref="ErrorBorderControl"/> class.
/// </summary>
public ErrorBorderControl()
{
InitializeComponent();
}
#endregion Ctors
#region Public Properties
/// <summary>
/// Gets or sets the validation object.
/// </summary>
/// <value>The validation object.</value>
public object ValidationObject
{
get { return (object)GetValue(ValidationObjectProperty); }
set { SetCurrentValue(ValidationObjectProperty, value); }
}
#endregion Public Properties
#region Private Methods
/// <summary>
/// Raises when the ValidationObject property value changes
/// </summary>
/// <param name="d">The d.</param>
/// <param name="e">The <see cref="DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
private static void OnValidationObjectChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((ErrorBorderControl)d).ValidationObject = e.NewValue;
}
#endregion Private Methods
}
Implementation:
<!-- Owner type -->
<itc:ErrorBorderControl
Grid.Row="1"
ValidationObject="{Binding Path=RecordType, ValidatesOnNotifyDataErrors=True}">
<StackPanel
Orientation="Horizontal">
<!-- Owner -->
<itc:ItecRadioButton
Content="{DynamicResource Language.SingleLine.Owner}"
Margin="0,4,4,4"
IsChecked="{Binding Path=RecordType, Converter={itc:EnumToBooleanConverter EnumValue={x:Static recordOwners:RecordOwnerRecordType.Owner}}}"/>
<!-- FAO -->
<itc:ItecRadioButton
Content="{DynamicResource Language.SingleLine.FAO}"
Margin="0,4,4,4"
IsChecked="{Binding Path=RecordType, Converter={itc:EnumToBooleanConverter EnumValue={x:Static recordOwners:RecordOwnerRecordType.Fao}}}"/>
<!-- Account Manager -->
<itc:ItecRadioButton
Content="{DynamicResource Language.SingleLine.Account_Manager}"
Margin="0,4,4,4"
IsChecked="{Binding Path=RecordType, Converter={itc:EnumToBooleanConverter EnumValue={x:Static recordOwners:RecordOwnerRecordType.AccountManager}}}"/>
<!-- Watcher -->
<itc:ItecRadioButton
Content="{DynamicResource Language.SingleLine.Watcher}"
Margin="0,4,4,4"
IsChecked="{Binding Path=RecordType, Converter={itc:EnumToBooleanConverter EnumValue={x:Static recordOwners:RecordOwnerRecordType.Watcher}}}"/>
</StackPanel>
</itc:ErrorBorderControl>
Output:
Invalid Selection
Notice that it looks like the template, although defined inside the user control, is affecting other Border controls inside other control. Look at when I have made the selection:
Valid Selection
The controls left in red do not event take part in validation. How does a control template inside another control affect all Borders? I just don't get it. What I need to do is to define a template that I can apply to the control I want it to be applied to only, and to be able to re-use it.
Related
In a mvvm application some areas inside a window (in reality it is a UserControl inside MainWindow) are dynamically displayed according to the user selections.
The changing blocks are inside Stackpanels, I have 4 of them and only one at a time is displayed. This is accomplished binding Visibility to a bool property and using the BooleanToVisibilityConverter.
I put all the alternate StackPanel inside parent control. It works correctly, but during design phase in Visual Studio I see all of them, so I have problems in figuring the final layout.
How can I easily create the layout having more controls which share the same window area and are displayed one at a time ?
Setting A Design Time Only Data Context
Developing XAML in the Studio Designer can be greatly simplified by setting the Design-Time Data Context.
One implementation is based on setting a duplicate DataContext which will be ignored during the final compilation.
To implement the switching, add to the ViewModel, a property that will inform the designer whether it can be used in Development Mode or not.
I use an MVVMLight situation for this example, but for this declared instance property IsInDesignMode and static property ViewModelBase.IsInDesignModeStatic.
Example:
using System.ComponentModel;
namespace DataContextDesignTime.Example
{
public class MyViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool _flag;
public bool Flag
{
get => _flag;
set
{
if (!Equals(_flag, value))
{
_flag = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Flag)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(NotFlag)));
}
}
}
public bool NotFlag => !Flag;
}
}
<Window x:Class="DataContextDesignTime.Example.ExamleWindow"
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:DataContextDesignTime.Example"
mc:Ignorable="d"
Title="ExamleWindow" Height="450" Width="800">
<d:Window.DataContext>
<local:MyViewModel Flag="True" NotFlag="True"/>
</d:Window.DataContext>
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</Window.Resources>
<StackPanel>
<Border Background="LightBlue" Height="200"
Visibility="{Binding Flag, Converter={StaticResource BooleanToVisibilityConverter}}"/>
<Border Background="LightGreen" Height="400"
Visibility="{Binding NotFlag, Converter={StaticResource BooleanToVisibilityConverter}}"/>
</StackPanel>
</Window>
In this example, you can change property values in XAML or in the Property Browser.
And you will immediately see the work of your bindings, triggers, how the display for certain data changes.
Note
This may fail on more complex VMs/packages, but in general by setting the DataContext at design time is not difficult.
I need to recompile the project to see the changes in the properties.
The XAML Designer panel has an «Enable/Disable Project Code» button.
, but during design phase in Visual Studio I see all of them, so I have problems in figuring the final layout.
This problem is easily resolved by bringing up the Document Outline tab in visual studio. Once open, navigate to the visible tree and toggle the eyeball to visibly hide/unhide the control[s] one is not interested in; during design time only.
I'm developing my first WPF custom control and I'm facing some problems, here's a simplified version of the code I'm currently using:
using System.Windows;
using System.Windows.Controls;
namespace MyControls
{
[TemplatePart(Name = "PART_Button", Type = typeof (Button))]
public class MyControl : Control
{
public static readonly DependencyProperty ContentProperty = DependencyProperty.Register("Content", typeof (object), typeof (MyControl), new PropertyMetadata(null, OnLabelPropertyChanged));
private Button _buttonElement;
public object Content
{
get { return this.GetValue(LabelProperty); }
set { this.SetValue(ContentProperty, value); }
}
static MyControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof (MyControl), new FrameworkPropertyMetadata(typeof (MyControl)));
}
private static void OnContentPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
MyControl myControl = sender as MyControl;
if (myControl != null && myControl._buttonElement != null)
myControl._buttonElement.Content = e.NewValue;
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
this._buttonElement = this.Template.FindName("PART_Button", this) as Button;
}
}
}
This is the template for my custom control:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyControls">
<Style TargetType="{x:Type local:MyControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MyControl}">
<Button x:Name="PART_Button" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
Then i put it inside a Grid and try to set its Content property:
<Grid x:Name="layoutRoot">
<controls:MyControl x:Name="myControl" />
</Grid>
Here's the code behind:
using System.Windows;
namespace MyControls
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
this.InitializeComponent();
this.myControl.Content = "test";
}
}
}
This doesn't work, for some reason the OnContentPropertyChanged callback is called before OnApplyTemplate, so myControl._buttonElement is assigned too late and it's still null when trying to set its content. Why is this happening and how can I change this behavior?
I also need to provide full design time support but I cannot find a way to make my custom control accept some additional markup, much like the Grid control does with ColumnDefinitions:
<Grid x:Name="layoutRoot">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
</Grid>
Any help would be greatly appreciated!
UPDATE
I found a document that explains why the OnApplyTemplate method is called after control properies are set:
http://msdn.microsoft.com/en-us/library/dd351483%28v=vs.95%29.aspx
So the question is: how can I keep track of the properties that are set (in XAML or programmatically) and methods that are called when the control has not been initialized, so that I can set/call them when the OnApplyTemplate method is called? How can the same callback/method work both before and after the control initialization without duplicating the code?
UPDATE:
Instead of your "property" pushing changes in value into elements in your template by looking for Parts, you should instead have your template bind to properties on the control being templated.
Normally this is done using presenters within a template e.g. ContentPresenter binds to the property designated as the "content" (it finds out that name by looking for the [ContentProperty] attribute), and by using bindings in your template that use TemplateBinding or TemplatedParent to connect to the properties on your custom control.
Then there is no issue about what order you set your properties and when the template is applied....because it is the template that provides the "look" for the data/properties set on your control.
A custom control should only really need to know and interact with "parts" if it needs to provide certain behaviour/functionality e.g. hooking the click event on a button "part".
In this case instead of setting the Content in the constructor in code-behind, you should get your template to bind to the property. The example I gave below showed how that was generally done with a Content property.
Alternatively you could pull out properties more explicitly e.g. this could be inside your template.
<Label Content="{TemplateBinding MyPropertyOnMyControl}" .....
<Button Content="{TemplateBinding AnotherPropertyOnMyControl}" .....
I think it would be better to designate your "content" using [ContentProperty] attribute, and then using a ContentPresenter in your template so that it can be injected inside your Button, rather than you hooking your Content DependencyProperty. (if you inherit from ContentControl then that provides the "content" behaviour).
[TemplatePart(Name = "PART_Button", Type = typeof (Button))]
public class MyControl : Control
[ContentProperty("Content")]
and
<ControlTemplate TargetType="{x:Type local:MyControl}">
<Button x:Name="PART_Button">
<ContentPresenter/>
</Button>
</ControlTemplate>
As for you wanting to be able to specify some design time data via XAML like Grid does with ColumnDefinition....well that is just using Property Element syntax to specify the items to fill an IList/ICollection typed property.
So just create your own property that can hold a collection of the type you accept e.g.
public List<MyItem> MyItems { get; set; } // create in your constructor.
I have encountered similar problem, OnApplyTemplate was called before OnLoaded.
The problem was due to binding in xaml. I bound boolean property to Checked instead of IsChecked on one of my checkboxes.
I searched the archives for help but I can't find anything quite specific enough for my particular issue.
I have a TreeView using MVVM to bind data and all seems good. I want to extend the functionality such that I think using a user control for the TreeView items would be good.
Here is the XAML code for the hierarchical data template used by the TreeViewItems:
<HierarchicalDataTemplate
DataType="{x:Type vm:SiteViewModel}"
ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding SiteName}"/>
</StackPanel>
</HierarchicalDataTemplate>
I want to replace the TextBlock with my user control:
<uc:MyTextBlock InternalText="{Binding SiteName}"/>
The user control (for now) just contains another TextBlock and has a dependency property called InternalText, i.e.
<TextBlock Text="{Binding Path=InternalText}" />
and I set the DataContext in the constructor of the user control to itself:
public MyTextBlock ()
{
InitializeComponent();
DataContext = this;
}
This isn't working, but if I just change the template so that it specifies static text it seems to work fine:
<uc:MyTextBlock InternalText="Some site name"/>
So how do I get the bound data to get passed to the user control? It's probably something simple but I'm new to WPF so I've not worked it out yet.
Thanks!
In the codebehind
public class MyUserControl : UserControl
{
#region Text
/// <summary>
/// The <see cref="DependencyProperty"/> for <see cref="Text"/>.
/// </summary>
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register(
RunningPropertyName,
typeof(string),
typeof(MyUserControl ),
new UIPropertyMetadata(null));
/// <summary>
/// The name of the <see cref="Text"/> <see cref="DependencyProperty"/>.
/// </summary>
public const string TextPropertyName = "Text";
/// <summary>
/// The text to display
/// </summary>
public string Text
{
get { return (string)GetValue(TextProperty ); }
set { SetValue(TextProperty , value); }
}
#endregion
}
In the xaml
<UserControl x:Class="Derp.MyUserControl"
x:Name="root"
SnippingXamlForBrevity="true"
and later...
<TextBlock Text="{Binding Text, ElementName=root}" />
InternalText seems to be a not dependency property. Try to convert it into one.
In WPF, what, really, does it mean to be a "dependency property"?
I read Microsoft's Dependency Properties Overview, but it's not really sinking in for me. In part that article says:
Styles and templates are two of the chief motivating scenarios for using dependency properties. Styles are particularly useful for setting properties that define application user interface (UI). Styles are typically defined as resources in XAML. Styles interact with the property system because they typically contain "setters" for particular properties, as well as "triggers" that change a property value based on the real-time value for another property.
And then the example code is this:
<Style x:Key="GreenButtonStyle">
<Setter Property="Control.Background" Value="Green"/>
</Style>
....
<Button Style="{StaticResource GreenButtonStyle}">I am green!</Button>
But I'm not getting what is special about this. Does it just imply, that when I set Style on the button to the given style, that I am actually setting Background implicitly? Is that the crux of it?
Here's the explanation for how dependency properties work that I always wished someone had written for me. It's incomplete and quite possibly wrong, but it will help you develop enough of an understanding of them that you can will be able to grasp the documentation that you read.
Dependency properties are property-like values that are get and set via methods of the DependencyObject class. They can (and generally do) look very much like CLR properties, but they're not. And this gets to the first confusing thing about them. A dependency property is really made up of a couple of components.
Here's an example:
Document is a property of the RichTextBox object. It's a real CLR property. That is to say, it's got a name, a type, a getter, and a setter, just like any other CLR property. But unlike "normal" properties, the RichTextBox property doesn't merely get and set a private value inside the instance. Internally, it's implemented like this:
public FlowDocument Document
{
get { return (FlowDocument)GetValue(DocumentProperty); }
set { SetValue(DocumentProperty, value); }
}
When you set Document, the value you passed in gets passed to SetValue, along with DocumentProperty. And what is that? And how does GetValue get its value? And ...why?
First the what. There's a static property defined on the RichTextBox named DocumentProperty. When this property is declared, it's done like this:
public static DependencyProperty DocumentProperty = DependencyProperty.Register(
"Document",
typeof(FlowDocument),
typeof(RichTextBox));
The Register method, in this case, tells the dependency property system that RichTextBox - the type, not the instance - now has a dependency property named Document of type FlowDocument. This method stores this information...somewhere. Where, exactly, is an implementation detail that's hidden from us.
When the setter for the Document property calls SetValue, the SetValue method looks at the DocumentProperty argument, verifies that it's really a property that belongs to RichTextBox and that value is the right type, and then stores its new value...somewhere. The documentation for DependencyObject is coy on this implementation detail, because you don't really need to know it. In my mental model of how this stuff works, I assume there's a property of type Dictionary<DependencyProperty, object> that's private to the DependencyObject, so derived classes (like RichTextBox) can't see it but GetValue and SetValue can update it. But who knows, maybe it's written on parchment by monks.
At any rate, this value is now what's called a "local value," which is to say it's a value that's local to this specific RichTextBox, just like an ordinary property.
The point of all this is:
CLR code doesn't need to know that a property is a dependency property. It looks exactly like any other property. You can call GetValue and SetValue to get and set it, but unless you're doing something with the dependency property system, you probably don't need to.
Unlike a normal property, something other than the object that it belongs to can be involved in getting and setting it. (You could do this with reflection, conceivably, but reflection is slow. Looking things up in dictionaries is fast.)
This something - which is the dependency property system - essentially sits between an object and its dependency properties. And it can do all kinds of things.
What kinds of things? Well, let's look at some use cases.
Binding. When you bind to a property, it has to be a dependency property. This is because the Binding object doesn't actually set properties on the target, it calls SetValue on the target object.
Styles. When you set an object's dependency property to a new value, SetValue tells the style system that you've done so. That's how triggers work: they don't find out that a property's value has changed through magic, the dependency property system tells them.
Dynamic resources. If you write XAML like Background={DynamicResource MyBackground}, you can change the value of the MyBackground resource, and the background of the object referencing it gets updated. This isn't magic either; the dynamic resource calls SetValue.
Animations. Animations work by manipulating property values. Those have to be dependency properties, because the animation is calling SetValue to get at them.
Change notification. When you register a dependency property, you can also specify a function that SetValue will call when it sets the property's value.
Value inheritance. When you register a dependency property, you can specify that it participate in property value inheritance. When you call GetValue to get the value of an object's dependency property, GetValue looks to see if there's a local value. If there's not, it traverses up the chain of parent objects looking at their local values for that property.
This is how it is that you can set the FontFamily on a Window and magically (I'm using that word a lot) every control in the window uses the new font. Also, it's how it is that you can have hundreds of controls in a window without each of them having a FontFamily member variable to track their font (since they don't have local values) but you can still set the FontFamily on any one control (because of the seekrit hidden dictionary of values that every DependencyObject has).
In WPF, what, really, does it mean to be a "dependency property"?
In order to be a dependency property, the property must actually be defined as a DependencyProperty, statically, on the class. The dependency property system is very different than a standard CLR property.
Dependency properties are handled very differently, though. A type defines a dependency property statically, and provides a default value. The runtime actually doesn't generate a value for an instance until it's needed. This provides one benefit - the property doesn't exist until requested for a type, so you can have a large number of properties without overhead.
This is what makes the styling work property, but is also important to allow attached properties, property "inheritance" through the visual tree, and many other things WPF relies on.
For example, take the DataContext dependency property. Typically, you set the DataContext dependency property for a Window or a UserControl. All of the controls within that Window, by default, "inherit" their parent's DataContext proeprty automatically, which allows you to specify data bindings for controls. With a standard CLR property, you'd need to define that DataContext for every control in the window, just to get binding to work properly.
It may be helpful to understand what problem the dependency property is trying to solve.
If we put the Binding, Animation and the Change Event model to one side as they've been discussed in other answers, the benefit is memory usage and thus scalability to host many thousand WPF objects in a window.
If a window contains 1000 Label objects with each Label object having the usual Foreground, Background, FontFamily, FontSize, FontWeight, etc., then traditionally this would consume memory because each property would have a private backing field to store the value.
Most applications will change only a few properties, the majority of which will be left at their default values. Basically very wasteful and redundant information (each object just holding the same default values in memory)
This is where dependency properties are different.
// Lets register the Dependency Property with a default value of 20.5
public static readonly DependencyProperty ColumnWidthProperty =
DependencyProperty.Register("ColumnWidth", typeof(double), typeof(MyWPFControl), new UIPropertyMetadata(20.5, ColWitdhPropChanged));
public double ColumnWidth
{
get { return (double)GetValue(ColumnWidthProperty); }
set { SetValue(ColumnWidthProperty, value); }
}
There is no private backing field. When the dependency property is registered a default value can be specified. So in most cases the returned value from GetValue is the default value that has only been stored the once to cover all instances of the Label object across all windows of your application.
When a dependency property is set using the SetValue it stores the non-default value in a collection identified by the object instance, to be returned in all subsequent GetValue calls.
This storage method will therefore only consume memory for the properties of the WPF objects that have changed from the default value. i.e. only the differences from the default value.
A simple/fundamental difference - Change Notification: Changes to Dependency Properties are reflected/refreshed in UI on changes whereas CLR properties don't.
<Window x:Class="SampleWPF.MainWindow"
x:Name="MainForm"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SampleWPF"
Title="About WPF Unleashed" SizeToContent="WidthAndHeight"
Background="OrangeRed"
>
<StackPanel DataContext="{Binding ElementName=MainForm}">
<!-- Bind to Dependency Property -->
<Label Name="txtCount1" FontWeight="Bold" FontSize="20" Content="{Binding ElementName=MainForm, Path=Count1, Mode=OneWay}" />
<!-- Bind to CLR Property -->
<Label Name="txtCount2" Content="{Binding ElementName=MainForm, Path=Count2, Mode=OneWay}"></Label>
<!-- Bind to Dependency Property (Using DataContext declared in StackPanel) -->
<Label Name="txtCount3" FontWeight="Bold" FontSize="20" Content="{Binding Count1}" />
<!-- Child Control binding to Dependency Property (Which propagates down element tree) -->
<local:UserControl1 />
<!-- Child Control binding to CLR Property (Won't work as CLR properties don't propagate down element tree) -->
<local:UserControl2 />
<TextBox Text="{Binding ElementName=txtCount1, Path=Content}" ></TextBox>
<TextBox Text="{Binding ElementName=txtCount2, Path=Content}" ></TextBox>
<Button Name="btnButton1" Click="btnButton1_Click_1">Increment1</Button>
<Button Name="btnButton2" Click="btnButton1_Click_2">Increment2</Button>
</StackPanel>
</Window>
<UserControl x:Class="SampleWPF.UserControl1"
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">
<StackPanel>
<Label Content="{Binding Count1}" ></Label>
<!--
<Label Content="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=Count1}"></Label>
-->
</StackPanel>
</UserControl>
<UserControl x:Class="SampleWPF.UserControl2"
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">
<StackPanel>
<Label Content="{Binding Count2}" ></Label>
<!--
<Label Content="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=Count2}"></Label>
-->
</StackPanel>
</UserControl>
And the code behind here (To declare the CLR and Dependency property):
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 SampleWPF
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public static readonly DependencyProperty Count1Property;
private int _Count2 = 2;
public int Count2
{
get { return _Count2; }
set { _Count2 = value; }
}
public MainWindow()
{
return;
}
static MainWindow()
{
// Register the property
MainWindow.Count1Property =
DependencyProperty.Register("Count1",
typeof(int), typeof(MainWindow),
new FrameworkPropertyMetadata(1,
new PropertyChangedCallback(OnCount1Changed)));
}
// A .NET property wrapper (optional)
public int Count1
{
get { return (int)GetValue(MainWindow.Count1Property); }
set { SetValue(MainWindow.Count1Property, value); }
}
// A property changed callback (optional)
private static void OnCount1Changed(
DependencyObject o, DependencyPropertyChangedEventArgs e) {
}
private void btnButton1_Click_1(object sender, RoutedEventArgs e)
{
Count1++;
}
private void btnButton1_Click_2(object sender, RoutedEventArgs e)
{
Count2++;
}
}
}
Another feature provided by Dependency Properties is value inheritance - value set in top level elements propagates down the element tree - In following example taken from http://en.csharp-online.net, FontSize and FontStyle declared on "Window" tag is applied to all child elements underneath:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
Title="About WPF Unleashed" SizeToContent="WidthAndHeight"
FontSize="30" FontStyle="Italic"
Background="OrangeRed">
<StackPanel>
<Label FontWeight="Bold" FontSize="20" Foreground="White">
WPF Unleashed (Version 3.0)
</Label>
<Label>© 2006 SAMS Publishing</Label>
<Label>Installed Chapters:</Label>
<ListBox>
<ListBoxItem>Chapter 1</ListBoxItem>
<ListBoxItem>Chapter 2</ListBoxItem>
</ListBox>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Button MinWidth="75" Margin="10">Help</Button>
<Button MinWidth="75" Margin="10">OK</Button>
</StackPanel>
<StatusBar>You have successfully registered this product.</StatusBar>
</StackPanel>
</Window>
References:
http://www.codeproject.com/Articles/29054/WPF-Data-Binding-Part-1
http://en.csharp-online.net/WPF_Concepts%E2%80%94Property_Value_Inheritance
How would I bind the IsChecked member of a CheckBox to a member variable in my form?
(I realize I can access it directly, but I am trying to learn about databinding and WPF)
Below is my failed attempt to get this working.
XAML:
<Window x:Class="MyProject.Form1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Title" Height="386" Width="563" WindowStyle="SingleBorderWindow">
<Grid>
<CheckBox Name="checkBoxShowPending"
TabIndex="2" Margin="0,12,30,0"
Checked="checkBoxShowPending_CheckedChanged"
Height="17" Width="92"
VerticalAlignment="Top" HorizontalAlignment="Right"
Content="Show Pending" IsChecked="{Binding ShowPending}">
</CheckBox>
</Grid>
</Window>
Code:
namespace MyProject
{
public partial class Form1 : Window
{
private ListViewColumnSorter lvwColumnSorter;
public bool? ShowPending
{
get { return this.showPending; }
set { this.showPending = value; }
}
private bool showPending = false;
private void checkBoxShowPending_CheckedChanged(object sender, EventArgs e)
{
//checking showPending.Value here. It's always false
}
}
}
<Window ... Name="MyWindow">
<Grid>
<CheckBox ... IsChecked="{Binding ElementName=MyWindow, Path=ShowPending}"/>
</Grid>
</Window>
Note i added a name to <Window>, and changed the binding in your CheckBox. You will need to implement ShowPending as a DependencyProperty as well if you want it to be able to update when changed.
Addendum to #Will's answer: this is what your DependencyProperty might look like (created using Dr. WPF's snippets):
#region ShowPending
/// <summary>
/// ShowPending Dependency Property
/// </summary>
public static readonly DependencyProperty ShowPendingProperty =
DependencyProperty.Register("ShowPending", typeof(bool), typeof(MainViewModel),
new FrameworkPropertyMetadata((bool)false));
/// <summary>
/// Gets or sets the ShowPending property. This dependency property
/// indicates ....
/// </summary>
public bool ShowPending
{
get { return (bool)GetValue(ShowPendingProperty); }
set { SetValue(ShowPendingProperty, value); }
}
#endregion
You must make your binding mode as TwoWay :
<Checkbox IsChecked="{Binding Path=ShowPending, Mode=TwoWay}"/>
If you have only one control that you want to bind to a property of your code-behind, then you can specify this as the source in your binding via a RelativeSource like this:
<CheckBox ...
IsChecked="{Binding ShowPending, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}">
That could be the end of the answer. But more generally you will have multiple controls and wish to bind them to various properties on your class. In this case it is neater and more convenient to make use of the fact that the DataContext property (which is the default source object for data binding) is inherited down through the control hierarchy, so setting it at the top level will make it available to all the child controls.
There is no default value for DataContext, but there are at least two ways you can set the DataContext property of your Window element to point at itself:
By setting DataContext = this in the code-behind constructor. This is very simple, but some might argue that it's not clear in the XAML where the DataContext is pointing.
By setting the DataContext in XAML using DataBinding
The simplest and, I think, most elegant way to set the DataContext at the Window/UserControl level in XAML is very straight forward; just add DataContext="{Binding RelativeSource={RelativeSource Self}}" to your Window element. RelativeSource Self just means "bind directly to the object", which in this case is the Window object. The lack of a Path property results in the default Path, which is the source object itself (i.e. the Window).
<Window x:Class="MyProject.Form1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<CheckBox ...
IsChecked="{Binding ShowPending}">
</CheckBox>
</Grid>
</Window>
Once you have done this, the DataContext property for all child controls will be the Window class, so data binding to properties in your code-behind will be natural.
If for some reason you don't want to set the DataContext on the Window, but wish to set it lower down the control hierarchy, then you can do so by using the FindAncestor mechanism. E.g. if you want to set it on the Grid element and all children of the Grid:
<Grid DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}">
<CheckBox ...
IsChecked="{Binding ShowPending}">
</CheckBox>
</Grid>
It's probably worth noting at this point that what we have achieved so far is the ability to bind a UI Control to a property of your code-behind class, and for that code-behind property to be kept up-to-date with changes to the UI element. So if the user checks the CheckBox, the ShowPending property will be updated.
But quite often you also want the reverse to be true; a change to the source property should be reflected in a corresponding change to the UI Control. You can see this by adding another CheckBox control to your window, bound to the same ShowPending property. When you click one checkbox, you would probably hope or expect the other Checkbox to be synchronized, but it won't happen. To achieve this your code-behind class should either (a) implement INotifyPropertyChanged, (b) add a ShowPendingChanged event or (c) make ShowPending a Dependency Property. Of the 3, I suggest implementing INotifyPropertryChanged on your code-behind is the most common mechanism.