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.
Related
I'm trying to understand dependency property and learn how to use it. I'm going through articles and in this article https://www.c-sharpcorner.com/UploadFile/6d590d/wpf-dependency-property/ there's this example:
MainWindow.xaml:
<Window x:Class="WpfApplication1.DependencyPropertyDemo" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfApplication1" Title="DependencyPropertyDemo" Height="350" Width="525">
<Window.Resources>
<ResourceDictionary>
<local:CarDependencyClass x:Key="carDependencyClass"></local:CarDependencyClass>
</ResourceDictionary>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Label Content="Enter Car:" Grid.Row="0" VerticalAlignment="Center" />
<TextBox Text="{Binding Path=MyCar, Source={StaticResource carDependencyClass }}" Name="MyTextCar" Height="25" Width="150" />
<Button Name="MyButton" Content="Click Me!" Height="25" Click="MyButton_Click" Width="150" Grid.Row="1" />
</Grid>
</Window>
MainWindow.xaml.cs:
using System.Windows;
namespace WpfApplication1 {
/// <summary>
/// Interaction logic for DependencyPropertyDemo.xaml
/// </summary>
public partial class DependencyPropertyDemo : Window {
public DependencyPropertyDemo() {
InitializeComponent();
}
private void MyButton_Click(object sender, RoutedEventArgs e) {
CarDependencyClass dpSample = TryFindResource("carDependencyClass") as CarDependencyClass;
MessageBox.Show(dpSample.MyCar);
}
}
public class CarDependencyClass : DependencyObject {
//Register Dependency Property
public static readonly DependencyProperty CarDependencyProperty = DependencyProperty.Register("MyProperty", typeof(string), typeof(CarDependencyClass));
public string MyCar {
get {
return (string)GetValue(CarDependencyProperty);
}
set {
SetValue(CarDependencyProperty, value);
}
}
}
}
It works. I noticed that they registered dependency property with the name "MyProperty" and that it isn't used anywhere in the program. Only normal CLR property MyCar is used in xaml.
But then there's another article https://www.c-sharpcorner.com/article/simplest-wpf-dependency-property-for-beginners-on-background-color/. And they provide other example:
MainWindow.xaml:
<Window x:Class="DependencyPropertyTutorial.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:views="clr-namespace:DependencyPropertyTutorial" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:DependencyPropertyTutorial" mc:Ignorable="d" Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<SolidColorBrush x:Key="BG" Color="Green" />
</Window.Resources>
<Grid>
<views:CustomButtonControl SetBackground="{DynamicResource BG}"></views:CustomButtonControl>
</Grid>
</Window>
CustomButtonControl.xaml:
<UserControl x:Class="DependencyPropertyTutorial.CustomButtonControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:DependencyPropertyTutorial"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Button x:Name="btnCustom" Content="Button" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="75" Height="52" Click="btnCustom_Click" />
</Grid>
</UserControl>
CustomButtonControl.xaml.cs:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace DependencyPropertyTutorial {
/// <summary>
/// Interaction logic for CustomButtonControl.xaml
/// </summary>
public partial class CustomButtonControl : UserControl {
public CustomButtonControl() {
InitializeComponent();
}
public static readonly DependencyProperty btnDependencyProperty = DependencyProperty.Register("SetBackground", typeof(SolidColorBrush), typeof(CustomButtonControl), new PropertyMetadata(new SolidColorBrush(Colors.HotPink), new PropertyChangedCallback(OnSetColorChanged)));
public SolidColorBrush SetBackground {
set {
SetValue(btnDependencyProperty, value);
}
get {
return (SolidColorBrush)GetValue(btnDependencyProperty);
}
}
private void btnCustom_Click(object sender, RoutedEventArgs e) {
this.SetBackground = new SolidColorBrush(Colors.IndianRed);
}
private static void OnSetColorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
CustomButtonControl mycontrol = d as CustomButtonControl;
mycontrol.callmyInstanceMethod(e);
}
private void callmyInstanceMethod(DependencyPropertyChangedEventArgs e) {
btnCustom.Background = (SolidColorBrush)e.NewValue;
}
}
}
And here they register dependency property with the name "SetBackground" which is exactly the same as the name of CLR property - SetBackground. And if I change dependency property(the one I registered with Register method) "SetBackground" to something else, like "SetBackgroundDependencyProperty", then I get a XAML exception when trying to run the application. But "SetBackground" dependency property isn't even referenced in xaml anywhere. Only CLR property SetBackground is referenced in XAML at the line
<views:CustomButtonControl SetBackground="{DynamicResource BG}"></views:CustomButtonControl>
I also get an error in Visual Studio with this example:
But when I try to build and run the application, it works.
So my questions are: why in the first example they didn't have to name registered dependency property the same as CLR property, but in the second example, I have to name registered dependency property the same as CLR property. Is there a way to name registered dependency property differently to the CLR property in the second example? Why and how xaml even uses dependency properties, considering xaml references only CLR properties anyway? I checked it and in both projects only CLR properties are referenced from XAML, according to VS IntelliSense. Why do I have to register dependency property with the same name as CLR property - "SetBackground" when in xaml only CLR property is referenced and it returns SolidColorBrush from the dependency property, anyway:
return (SolidColorBrush)GetValue(btnDependencyProperty);
Here's the solution with both examples:
https://github.com/KulaGGin/DependencyProperty
First example is a bit dirty, I wouldn't code it such way. There is a good convention to avoid a confusion - to name the DP as the CLR property + 'Property'(but it's not mandatory!) and register it as name of CLR property(if you want to use it as DP in XAML).
First to your questions:
First example does work, because of everywhere, where the property MyCar being used, it is used as CLR property. If you will try to bind to the MyCar, it will fail, because of there is no such a dependency property. To implement the functionality in this example would be enough just to declare a CLR property:
public string MyCar { get; set; }
instead of all this confusion with dependency property.
In second example CLR property as well as dependency property SetBackground are defined (the name btnDependencyProperty for the field is not convenient, but OK). Missunderstanding on your side, is what being used in XAML.
If you use in XAML Binding or DynamicResource the dependency property as well as CLR property are necessary! Therefore they need to have the same name. If there aren't, then you will get an error.
If you set the property to the StaticResource or directly to the value or even do not use it in XAML, then you will be able to run the application.
DependencyProperty implementation is slightly weird.
The XAML compiler depends on the CLR property wrapper in order to compile, but at runtime bindings ignore it completely and just call GetValue/SetValue on the DP. Therefore the names should match.
There was a typo in the first example. The registered dependency property name needs to be the same as the CLR-backing property.
Declaring a DependencyProperty is a two-stage process:
Register the property so that the WPF DependencyProperty system can track it and notify when properties change, etc.
Setup a CLR property that gives developers an API to get and set values.
I hope this helps.
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.
I'm trying to get a UserControl working in Windows Phone 7. I have a few properties which I'd like to bind to, yet they aren't populated regardless of whether I add them as DependencyProperties or not. The only way I can get them to work is by setting the DataContext instead. The code I've tried is (for one property):
public static readonly DependencyProperty MaximumItemsProperty = DependencyProperty.Register("MaximumItems", typeof(int), typeof(ManageIngredientsControl), new PropertyMetadata(0));
/// <summary>
/// Gets or sets the maximum number of items to match.
/// </summary>
/// <value>The maximum number of items to match.</value>
public int MaximumItems
{
get { return Convert.ToInt32(base.GetValue(MaximumItemsProperty)); }
set { base.SetValue(MaximumItemsProperty, value); }
}
<TextBox Grid.Row="1" Grid.Column="1" x:Name="nudMaxIngredients" Width="120" Text="{Binding MaximumItems,Mode=TwoWay,ElementName=root}" InputScope="Number" />
The root UserControl element is called 'root', but the value isn't populated. The only way to get it half working is by using this:
public int MaximumItems
{
get { return Convert.ToInt32(DataContext) }
set { DataContext = value; }
}
It seems something's interfering with the DataContext, but if I'm binding to DependencyProperties why would it matter?
I'm guessing that your TextBox is inside your UserControl. If so, then ElementName binding has an issue, as described here.
Basically, the name you give the UserControl in it's XAML is overwritten by any name given to it where it's used (i.e. in your Page).
The workaround is to use something like:
<TextBox Grid.Row="1" Grid.Column="1" x:Name="nudMaxIngredients" Width="120" Text="{Binding Parent.MaximumItems,Mode=TwoWay,ElementName=LayoutRoot}" InputScope="Number" />
Where LayoutRoot is the root control inside the UserControl's XAML.
Also, your first approach to the MaximumItems property is correct.
I have to create Silverlight User Control with public properties that should be used in inner controls.
public partial class MyControl : UserControl
{
public static readonly DependencyProperty MyCustomProperty =
DependencyProperty.Register(
"MyCustom", typeof(string), typeof(MyControl),
new PropertyMetadata("defaultValue"));
public string MyCustom
{
...
}
I tried several ways to bind, but all fail - dependency property is not seen for some reason.
For example this straightforward binding fails:
<UserControl x:Class="...MyControl"
...
x:Name="mc"
>
<Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<Image Source="{Binding Path=MyCustom, Mode=OneWay, ElementName=mc}" />
</Grid>
</UserControl>
What I'am doing wrong?
What you are doing is not a good pattern. The UserControl does not really "own" the name property. If another UserControl or Page were to place an instanced of your MyControl in its Xaml, it can give it name other than "mc", at which point your code is broken.
Instead use this approach:-
<UserControl x:Class="...MyControl"
>
<Grid x:Name="LayoutRoot" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<Image Source="{Binding Path=Parent.MyCustom, Mode=OneWay, ElementName=LayoutRoot}" />
</Grid>
</UserControl>
That out of the way you main problem is that the Image Source property is of type ImageSource not string. You get to use a string literal in Xaml because the Xaml parser does some parser magic the converts the string to an ImageSource. This doesn't happen when using binding.
Change you controls property to:-
public partial class MyControl : UserControl
{
public static readonly DependencyProperty MyCustomProperty =
DependencyProperty.Register(
"MyCustom", typeof(ImageSource), typeof(MyControl),
new PropertyMetadata(null));
[TypeConverter(typeof(ImageSourceConverter))]
public ImageSource MyCustom
{
...
}
Now in another UserControl or Page where you MyControl is hosted you can use a string to assign this MyCustom property. However in code you need to create an instance of something like BitmapImage to assign to this property.
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.