UserControl doesn't show validation exception in Silverlight - wpf

I have a user control and I have bound its dependency property TextValue to a View Model RightSpecGlassStrength
UserControl code
<UserControl x:Class="NumericUpDown1"
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"
mc:Ignorable="d"
d:DesignHeight="25" d:DesignWidth="70">
<StackPanel Orientation="Horizontal" VerticalAlignment="Top" >
<TextBox x:Name="InputTextBox" Grid.Row="0" Grid.Column="0" Grid.RowSpan="1"
Style="{StaticResource NumericUpDownTextBoxStyle}"
KeyDown="InputTextBox_KeyDown"
KeyUp="InputTextBox_KeyUp"
GotFocus="InputTextBox_GotFocus"
LostFocus="InputTextBox_LostFocus"
MouseWheel="InputTextBox_MouseWheel"
MouseEnter="InputTextBox_MouseEnter"
TextInputStart="InputTextBox_TextInputStart"
LayoutUpdated="InputTextBox_LayoutUpdated"
/>
</StackPanel>
</UserControl>
ViewItems.Xaml
<userControl:NumericUpDown1 x:Name="RightSpecGlassStrengthUpDown" Maximum="28" Minimum="-28" Step="0.25" TextValue="{Binding RightSpecGlassStrength, Mode=TwoWay, ValidatesOnNotifyDataErrors=True, NotifyOnValidationError=True}" TabIndex="5" />
ViewItemsViewModel.cs
public class ViewItemsViewModel : EntityViewModel
{
#region constructor
public ViewItemsViewModel(){}
#endregion
#region properties
private Double rightSpecGlassStrength;
public Double RightSpecGlassStrength
{
get
{
return rightSpecGlassStrength;
}
set
{
rightSpecGlassStrength=value;
ValidateStrengths("RightSpecGlassStrength", RightSpecGlassStrength);
PropertyChangedHandler("RightSpecGlassStrength");
}
}
private void ValidateStrengths(string propertyName1, double RightSpecGlassStrength)
{
ClearErrorFromProperty(propertyName1);
if (RightSpecGlassStrength == 0)
AddErrorForProperty(propertyName1, "Value can not be 0");
}
#endregion
}
My EntityViewModel.cs is implementing INotifyDataErrorInfo interface and inherit ViewModelBase class
public class EntityViewModel : ViewModelBase, INotifyDataErrorInfo
{
}
ViewModelBase.cs implements INotifyPropertyChanged
public class ViewModelBase : INotifyPropertyChanged
{
}
My code works fine when I bind the textbox or other silverlight control to the viewmodel.
and shows proper validation exception on the control.
But when the user control gets a validation exception the control does not show any exception.
I'm not getting whats wrong with user control.???

It's difficult to fully debug the issue without the code behind for the user control, however, it seems to me that you have a dependency property on the code behind like this:
public static DependencyProperty TextValueProperty = DependencyProperty.Register("TextValue", typeof(string), typeof(NumericUpDown1), null)
You then seem to be using the TextInputStart, KeyDown, KeyUp events to capture changes to the underlying control. My hunch is that either:
a) You're failing to update the TextValue property to the new value.
b) Your code behind is interfering with the validation process.
I would suggest that, instead of code behind, you name the user control in the xaml; i.e.
<UserControl x:Class="NumericUpDown1" x:Name="View"> ... </UserControl>
Then bind the text value of the underlying TextBox directly to the dependency property, like this:
<TextBox Text="{Binding ElementName=View, Path=TextValue, Mode=TwoWay, ValidatesOnNotifyDataErrors=True, NotifyOnValidationError=True}" ... />
This should then allow the validation mechanism to proceed as normal.
Hope it helps.

Related

How to show floating virtual keyboard (user control) in MainWindow when an input control (from another user control) has been set to focus in WPF?

I have been doing development work in WPF application which uses an MVVM pattern for a couple of days now. I'm very new to WPF and MVVM pattern as well.
In my scenario, I have a user control view (named EPayView.xaml) which has a textbox that will accept a phone number. The view has a corresponding viewmodel (named EPayViewModel.cs). In the MainWindow.xaml, I have a user control (floating virtual keyboard) which is derived from namespace controls WpfKb.Controls. The MainWindow.xaml also has a corresponding viewmodel (named MainViewModel.cs)
Having said that, I have done research on how to use attached dependency properties which lead me to this solution. Set focus on textbox in WPF from view model (C#) which I believe this is where I could bind the property IsFocused in the textbox of EPayView.xaml.
Below are the codes that I have already incorporated in my solution.
EpayView.xaml (textbox xaml markup)
<TextBox Text="{Binding PhoneNo}" Grid.Row="5" Margin="10,0,10,0" VerticalContentAlignment="Center" FontSize="12" x:Name="Email" behaviors:FocusExtension.IsFocused="{Binding IsFocused, Mode=TwoWay}"/>
MainWindow.xaml (xaml markup)
<Window x:Class="SmartPole540.View.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:WpfKb.Controls;assembly=SmartPole.WpfKb"
xmlns:wpf="clr-namespace:WebEye.Controls.Wpf;assembly=WebEye.Controls.Wpf.WebCameraControl"
xmlns:utilities="clr-namespace:SoltaLabs.Avalon.Core.Utilities;assembly=SoltaLabs.Avalon.Core"
xmlns:userControls="clr-namespace:SoltaLabs.Avalon.View.Core.UserControls;assembly=SoltaLabs.Avalon.View.Core"
xmlns:square="clr-namespace:SmartPole.View.Square;assembly=SmartPole.View"
xmlns:view="clr-namespace:SmartPole.View;assembly=SmartPole.View"
Title="CitiPulse"
WindowStartupLocation="Manual"
PreviewMouseLeftButtonDown="Window_PreviewMouseLeftButtonDown"
Name="mainWindow">
<userControls:RollPanel.BottomContent>
<square:SquareView Canvas.Top="1010" DataContext="{Binding DataContext.SquareViewModel,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type userControls:RollPanel}}}"/>
</userControls:RollPanel.BottomContent>
<controls:FloatingTouchScreenKeyboard
x:Name="floatKb" Width="500" Height="250" PlacementTarget="{Binding ElementName=MainGrid}"
Placement="Center" AreAnimationsEnabled="False" Visibility="Visible"
IsOpen="{Binding IsChecked, ElementName=kbButton}"/>
</Window>
In the above code, the user control RollPanel.BottomContent host the EPayView.xaml view inside another view which is RollPanel.xaml
EpayViewModel.cs contains the static class FocusExtension for the IsFocused attached property (refer to this solution - Set focus on textbox in WPF from view model (C#)). And, EPayViewModel.cs already implemented INotifyPropertyChanged which is wrapped inside a concrete class ObservableObject that accepts type of T. This is also same with MainViewModel.cs
public class EPayViewModel : ObservableObject<EPayViewModel>, IPaymentViewModel, IActiveViewModel
{ ... }
public class MainViewModel : ObservableObject<MainViewModel>
{ ... }
As such, my goal is that when the textbox in EPayView.xaml has the focus, the floating virtual keyboard (floatKb) in the MainWindow.xaml will be shown.
I'm stuck on how to proceed (I was thinking if a call to FocusExtension static class in EPayViewModel inside my MainViewModel.cs will suffice?), any help is greatly appreciated.
Cheers,
As AnjumSKhan already said, to react to some event in a MVVM way, you'll have to use Command. Command can be called within an EventTrigger, you will need to add a Reference to System.Windows.Interactvity component.
Let's assume you have a simple View and View Model and you need to show this View when the TextBox in a MainWindow got focus.
View (NewWindow.xaml)
<Window x:Class="My.NewWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="NewWindow" Height="300" Width="300">
<TextBlock Text="{Binding Message}"/>
View Model
public class NewWindowViewModel
{
private string _message;
public string Message
{
get { return _message; }
set { _message = value; }
}
}
You also have a MainWindow, it is a main view for an app and it contains the target TextBox. You may see that there is an EventTrigger added to the TextBox and it has a property InvokeCommandAction which is binded to the MainWindowViewModel's command called ShowCommand.
Main Window
<Window x:Class="My.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Interactivity="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" Title="MainWindow" Height="350" Width="525">
<TextBox Height="40" Text="{Binding Text}">
<Interactivity:Interaction.Triggers>
<Interactivity:EventTrigger EventName="GotFocus">
<Interactivity:InvokeCommandAction Command="{Binding ShowCommand}"/>
</Interactivity:EventTrigger>
</Interactivity:Interaction.Triggers>
</TextBox>
In the Show method of MainWindowViewModel NewWindow view is created and got new NewWindowViewModel instance as a DataContext. RelayCommand class is presented in my answer to this question
MainWindowViewModel
public class MainWindowViewModel
{
private string _text;
public string Text
{
get { return _text; }
set { _text = value; }
}
private ICommand _increaseCommand;
public ICommand ShowCommand
{
get
{
if (_increaseCommand == null)
{
_increaseCommand = new RelayCommand(
p => true,
Show);
}
return _increaseCommand;
}
}
private void Show(object obj)
{
var w = new NewWindow();
var nvm = new NewWindowViewModel();
nvm.Message = "Test";
w.DataContext = nvm;
w.Show();
}
}
What is left is to create a new MainWindowViewModel and setup a DataContext for MainWindow.
public MainWindow()
{
InitializeComponent();
var mvm = new MainWindowViewModel();
mvm.Text = "Focus me!";
DataContext = mvm;
}
Hope it will help.

Extremely simple silverlight binding not working

This should be an extremely simple solution, but searching through the internet there seems to be multiple different ways to do binding and NONE seem to actually work.
I've created a simple application with a button, textbox and listbox. The user adds text to the textbox, clicks Add and I want the text to appear in the list box. Note that the Add button will create a Person with the firstname the text in the textbox and the last name "Jones". This is just to figure out how to get binding to actually work. I have the ObservableCollection but can't seem to even figure out how to put in the resource to the object within the class itself. Is this even possible? do I have to create a separate class to have a binding?
Here is the complete XMAL
<UserControl x:Class="simpleBinding.MainPage"
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:z="clr-namespace:simpleBinding"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Canvas x:Name="LayoutRoot" Background="White">
<Button Name="_b" Content="Add" Height="23" HorizontalAlignment="Left" VerticalAlignment="Top" Width="58" Canvas.Left="90" Canvas.Top="5" Click="OnAdd" />
<TextBox Name="_tb" Canvas.Left="12" Canvas.Top="4" Height="24" Width="72"></TextBox>
<ListBox Name="_list" Canvas.Left="18" Canvas.Top="41" Height="98" Width="190" />
</Canvas>
and here is the complete Code behind
namespace simpleBinding
{
public partial class MainPage : UserControl
{
public ObservableCollection<Person> PersonList = new ObservableCollection<Person> ();
public MainPage()
{
InitializeComponent();
}
private void OnAdd(object sender, RoutedEventArgs e)
{
PersonList.Add(new Person(_tb.Text, "Jones"));
}
}
public class Person
{
public string FirstName {private set; get;}
public string LastName {private set; get; }
public Person(string fName, string lName)
{
FirstName = fName;
LastName = lName;
}
}
}
thanks for any help,
chris
To illustrate Ravuthasamy's & aqwert's comments. You have to set a DataContext first. You can set this in DataContext or read how MVVM work (It's a good Silvelight binding pattern) :
c#
public MainPage()
{
InitializeComponent();
DataContext = this;
}
After you can bind the class properties to elements :
Xaml
<ListBox
ItemsSource="{Binding PersonList}"
Canvas.Left="18"
Canvas.Top="41"
Height="98"
Width="190" />
Following the timeline you can see that this has taken me a week to finally get to a solution. I post it here now in hopes that someone else won't waste this much time. There seems to be a lot of posts about how to deal with this issue and the examples are limited. They either show only C# or Xaml. Then CollectionChanged and PropertyChanged aren't dealt with in a single example.
This is a simple example, that implements both collection changed and property changed. As well as binding in Xaml
Here is the Xaml.
<UserControl x:Class="simpleBinding.MainPage"
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:src="clr-namespace:simpleBinding"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Canvas x:Name="LayoutRoot" Background="White" DataContext="{Binding}">
<Canvas.Resources>
<src:PersonList x:Key="myDataSource"></src:PersonList>
</Canvas.Resources>
<Button Name="_b" Content="Add" Height="23" HorizontalAlignment="Left" VerticalAlignment="Top" Width="58" Canvas.Left="90" Canvas.Top="5" Click="OnAdd" />
<Button Canvas.Left="150" Canvas.Top="5" Content="Edit" Height="23" Name="button1" Width="58" Click="OnEdit" />
<TextBox Name="_tb" Canvas.Left="12" Canvas.Top="4" Height="24" Width="72"></TextBox>
<ListBox Name="_list" Canvas.Left="18" Canvas.Top="41" Height="98" Width="190" ItemsSource="{Binding Source={StaticResource myDataSource}}" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=FirstName}" Margin="0,0,2,0" />
<TextBlock Text="{Binding Path=LastName}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Canvas>
Add a xmlns that will reference your code behind. In this case my namespace is xmlns:src then you can use VS intellisense to go to the correct class.
Add a resource to the layoutRoot item. In my case I'm using a canvas, but it could be Grid or Stackpanel etc.
With the resource declared, you can now set the ItemSource binding in the ListBox.
I've chosen to use a template to display the data which I think is really cool (best part of Xaml!) In this case there are two textBlocks but if my underlying data source had an image, I could have used this was well to graphically display the data. The binding for each textbox can be set because the exposed properties of the object are declared in the C# code. Which will be discussed next
C# Code behind
namespace simpleBinding
{
public partial class MainPage : UserControl
{
public PersonList m_pList = new PersonList();
public MainPage()
{
InitializeComponent();
_list.ItemsSource = m_pList;
m_pList.Add(new Person("John", "Doe"));
}
private void OnAdd(object sender, RoutedEventArgs e)
{
m_pList.Add(new Person("Jones", _tb.Text));
}
private void OnEdit(object sender, RoutedEventArgs e)
{
m_pList[1].FirstName = _tb.Text;
}
}
public class PersonList : ObservableCollection<Person> , INotifyPropertyChanged
{
public PersonList() : base() // need to call base on intialization otherwise the binded resource is not updated.
{
Add(new Person("Willa", "Cather"));
Add(new Person("Isak", "Dinesen"));
Add(new Person("Victor", "Hugo"));
Add(new Person("Jules", "Verne"));
}
}
public class Person : INotifyPropertyChanged
{
private string _fName;
private string _lName;
public event PropertyChangedEventHandler PropertyChanged;
public string FirstName
{
set
{
_fName = value;
NotifyPropertyChanged("FirstName");
}
get
{
return _fName;
}
}
public string LastName
{
set
{
_lName = value;
NotifyPropertyChanged("LastName");
}
get
{
return _lName;
}
}
public Person(string fName, string lName) : base()
{
FirstName = fName;
LastName = lName;
}
public override string ToString()
{
return String.Format("{0} {1}", FirstName, LastName);
}
private void NotifyPropertyChanged(String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
I've chosen to use the ObservableCollection because it implements INotifyCollectionChanged. The public variable is exposed which allows you to bind to the resource declared in the Xaml. (Better code, make the var private and have a property that exposes the variable through a get!)
The ListBox _List needs to have its ItemsSource property set in Code Behind!!! without this whenever you change the list (add, delete etc) the UI is not updated. AND in fact you do not need the binding in the ListBox at all because we set the source in Code behind it is nice however in that in the designer with this bound control you can see that the binding is working because there are four names added when instantiating the PersonList.
The ObservableCollection needs to have the INotifyCollectionChanged added. Without this, when a property is changed the UI is NOT changed.
The properties that are to be exposed to the UI need to be implement in the object that is contained within the ObservableCollection (in my case the class Person exposed both FirstName and LastName) and then these properties can be bound in the Xaml (see the textBlocks's)
INotifyPropertyChanged requires that you implement a PropertyChanged event i.e. public event PropertyChangedEventHandler PropertyChanged;
To actually fire that event the "Person" object needs to implement code to do that, which in my case is the NotifyPropertyChanged Method. Each time a property is set, I call this method, which in turn looks to see is the PropertyChanged event is not null, and if not, then it raises that event.
Here is the key to property changes, without adding the , INotifyPropertyChanged to the Observable collection PropertyChanged is null.
Hope this helps someone

Sharing data between usercontrol and main view

I have an UserControl(AutoComplete) with his own ViewModel. When I use the UserControl inside a window, it run well, connect to a service, and paint data correctly.
The UserControl datacontext is set via xaml, and is binded to a property of the main window viewModel.
Ok, now I want that the UserControl can load data from the main window view model. The thing is that, supposing the usercontrol loads countries. When I type in the Usercontrol it returns the list of countries and when I select one of them, i.e. "Spain", the SelectedItem property of the Usercontrols updates to "Spain". I want an object in main window viewModel to udate to "Spain" and vice-versa, if I update the country object in the main window viewmodel, the selecteditem of the user should update too.
How can I accomplish that
I have this in my mainview:
<amctrls:AmAutoCompleteView DataContext="{Binding controladorAutoCompleteCountry}" />
the user control loks like this:
<telerik:RadComboBox Margin="2,0,0,0" Grid.Row="0" Grid.Column="0"
IsEditable="True"
Name="RadCbo"
ItemsSource="{Binding objectList}"
DisplayMemberPath="{Binding fieldToShow}"
OpenDropDownOnFocus="True"
SelectedItem="{Binding selectedCountry, Mode=TwoWay}"
Text="{Binding searchText, Mode=TwoWay}"
IsTextSearchEnabled="False"
StaysOpenOnEdit="True" />
controladorAutoCompleteCountry is a property of my mainview wih is an instance of the usercontrol viewmodel.
The viewmodel of the main view manage addresses, and what I want is to bind an address country to the usercontrol in order to edit the address. If i have the usercontrol binded to an instance of its controller, how can I bind the Country object of the address?
If you need to make those 2 views independent which is good if you want to reuse your control, go with Event Aggregator or simple events. Whenever an item is selected in the user control, it will publish an event stating, something interesting has happened. Main viewmodel can subscribe to those events and do the required. A simple case would be a creating a static class with an event and RaiseEvent method, user control will RaiseEvent and main viewmodel with be subscribing the event. Data to be passed between them can be added to the event args.
It's a bit the other way around, but you can try something like this:
Have a MainView with a
combobox that is bound to the string property SelectedCountry and method ChangeCountry()
ContentControl that is bound to CountryInfoViewModel property SelectedCountryControl
You can now bind your combobox to the CountryInfoViewModel that is loaded in your MainView.
Below is an example that worked for me (note that I used caliburn micro here).
It basicly updates the CountryInfoViewModel/View when a different country has been selected.
You could improve the ChangeCountry method to get all the data, and of course improve the CountryInfoViewModel/View to show everything you want shown.
MainViewModel
class MainViewModel : Screen
{
#region fields
private BindableCollection<string> _listOfCountries;
private string _selectedCountry;
private CountryInfoViewModel _selectedCountryControl;
#endregion fields
#region properties
public BindableCollection<string> ListOfCountries
{
get
{
return new BindableCollection<string>
{
"France",
"Holland",
"Russia"
};
}
}
public string SelectedCountry
{
get { return _selectedCountry; }
set
{
_selectedCountry = value;
NotifyOfPropertyChange(() => SelectedCountry);
}
}
public CountryInfoViewModel SelectedCountryControl
{
get { return _selectedCountryControl; }
set
{
_selectedCountryControl = value;
NotifyOfPropertyChange(() => SelectedCountryControl);
}
}
#endregion properties
public MainViewModel()
{
SelectedCountry = "Holland";
ChangeCountry();
}
public void ChangeCountry()
{
SelectedCountryControl = new CountryInfoViewModel()
{
CountryName = SelectedCountry
};
}
}
MainView:
<UserControl x:Class="WpfModifyDifferentView.Views.MainView"
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>
<ComboBox x:Name="ChangeCountry" SelectedItem="{Binding SelectedCountry}" ItemsSource="{Binding ListOfCountries}"/>
<ContentControl x:Name="SelectedCountryControl"/>
</StackPanel>
</UserControl>
CountryInfoViewModel:
class CountryInfoViewModel : Screen
{
#region fields
private string _countryName;
#endregion fields
#region properties
public string CountryName
{
get { return _countryName; }
set
{
_countryName = value;
NotifyOfPropertyChange(() => CountryName);
}
}
#endregion properties
}
CountryInfoView:
<UserControl x:Class="WpfModifyDifferentView.Views.CountryInfoView"
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 Orientation="Vertical">
<TextBlock Text="You have chosen the country:"/>
<TextBlock x:Name="CountryName"/>
</StackPanel>
</UserControl>

Property Value Inheritance Not Working with UserControl

I'm trying to take advantage of Property Value Inheritance from a Window to a UserControl. As far as I understand, you can achieve this by declaring an attached DependencyProperty (in conjunction with the FrameworkPropertyMetadataOptions.Inherits option).
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:my="clr-namespace:WpfApplication1" Name="BobWindow">
<Grid>
<Label Content="MainWindow" />
<TextBox Height="23" HorizontalAlignment="Left" Margin="85,2,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" Text="{Binding Path=Test, ElementName=BobWindow}" />
<my:UserControl1 HorizontalAlignment="Left" Margin="157,108,0,0" x:Name="userControl11" VerticalAlignment="Top" />
</Grid>
</Window>
MainWindow.xaml.cs:
using System;
using System.Windows;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public static readonly DependencyProperty TestProperty = DependencyProperty.RegisterAttached
(
"Test",
typeof(String),
typeof(MainWindow),
new FrameworkPropertyMetadata
(
null,
FrameworkPropertyMetadataOptions.Inherits
)
);
public String Test
{
get { return (String)GetValue(TestProperty); }
set { SetValue(TestProperty, value); }
}
public MainWindow()
{
InitializeComponent();
Test = "Yip!";
}
}
}
UserControl1.xaml:
<UserControl x:Class="WpfApplication1.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" Name="BobControl">
<Grid>
<Label Content="UserControl1" />
<TextBox Height="23" HorizontalAlignment="Left" Margin="85,2,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" Text="{Binding Path=Test, ElementName=BobControl}" />
</Grid>
</UserControl>
UserControl1.xaml.cs:
using System;
using System.Windows;
using System.Windows.Controls;
namespace WpfApplication1
{
public partial class UserControl1 : UserControl
{
public static readonly DependencyProperty TestProperty = DependencyProperty.RegisterAttached
(
"Test",
typeof(String),
typeof(UserControl1),
new FrameworkPropertyMetadata
(
null,
FrameworkPropertyMetadataOptions.Inherits
)
);
public String Test
{
get { return (String)GetValue(TestProperty); }
set { SetValue(TestProperty, value); }
}
public UserControl1()
{
InitializeComponent();
}
}
}
I haven't been able to find an explicit example to achieve this. The use of RegisterAttached in MainWindow and UserControl1 is my best guess. There must be something I'm missing!
UPDATE
I'd like to be able to create my controls in an arbitrary structure, set the value at the top of the tree and have the default value trickle down (similar to how DataContext works). Is this possible when TestProperty isn't in a common ancestor class for MainWindow and UserControl1?
Also, I want to avoid referencing the source class, since sometimes it will be a Window but in other cases it might be the host control in Windows Forms. Is this possible?
RESOLVE
I think my confusion stemmed from wanting to use the syntax of a non-attached dependency property to achieve value inheritance. I wanted to use the following xaml:
<Window ... Test="Fred" />
And access the inherited value in UserControl with the following syntax:
string Value = this.Test;
However, according Microsoft's Property Value Inheritance page, if you wish to inherit property values, then it must be through an attached property.
If above the code was re-written properly (declare the property once, with static getter/setter methods) then my xaml would look like this:
<Window ... my:MainWindow.Test="Fred" />
And my code behind in UserControl would look like this:
string Value = MainWindow.GetTest( this );
It seems that you might be misunderstanding what value inheritance means. If you set a dependency property on a control the value of that property will be the same in the controls inside of it. You don't need to redeclare the property itself (that just creates another property that is completely distinct).
An example of inheritance:
<Window ...
xmlns:local="..."
local:MainWindow.Test="Lorem Ipsum">
<Button Name="button"/>
In code you then should be able to get the value on the button and it should be the same as on the window.
var test = (string)button.GetValue(MainWindow.TestProperty);
// test should be "Lorem Ipsum".
The mistake you made here is to declare the property twice. Just declare it in MainWindow, not also in UserControl1. Then declare static getter and setter methods like this in MainWindow:
public static string GetTest(DependencyObject obj)
{
return (String)obj.GetValue(TestProperty);
}
public static void SetTest(DependencyObject obj, string value)
{
obj.SetValue(TestProperty, value);
}
Get more info here about custom attached properties.
Now when a UserControl1 is somewhere in the element tree in MainWindow, try to do something like the following with UserControl1 after it has been initialized:
UserControl1 uc = this; // for example in a UserControl1 event handler
string test = MainWindow.GetTest(uc);
EDIT: You could as well define the property in UserControl1 or in any other class, and since it is an attached property, that class does not even have to be derived from DependencyObject.

WPF User vs Custom Control

I have a grid control that I use throughout the application. I would like to extend the grid control to include a context menu with one item "freeze/unfreeze columns". If I elect to use a custom control, I cannot implement this functionality within the control -- instead, I have to implement the functionality wherever I place my custom control. The other alternative is user control, in which I can implement all the necessary functionality within the control:
<Grid>
<dxg:GridControl Name="gridData" DataSource="{Binding}" dx:DXSerializer.StoreLayoutMode="All">
<dxg:GridControl.Resources></dxg:GridControl.Resources>
<dxg:GridControl.Columns />
<dxg:GridControl.View>
<dxg:TableView ShowGroupPanel="False" MouseRightButtonUp="TableView_MouseRightButtonUp">
<dxg:TableView.ColumnMenuCustomizations>
<dxb:BarButtonItem Name="freezeColButton" Content="Freeze Column(s)" dxb:BarItemLinkActionBase.ItemLinkIndex="0" ItemClick="freezeColButton_ItemClick" />
</dxg:TableView.ColumnMenuCustomizations>
</dxg:TableView>
</dxg:GridControl.View>
</dxg:GridControl>
</Grid>
Notice, the TableView.ColumnMenuCustomization tag includes the event handler for the freeze/unfreeze functionality.
However, the only issue with the user control is that I cannot access the underlying Grid's Columns property. For example, when I place my user control (defined above) in a window, I get an error (Error 25: The tag 'ExtendedGridControl.Columns' does not exist in XML namespace 'clr-namespace:UI.Controls'):
<Window>
...
<Grid>
<uc:ExtendedGridControl x:Name="extendedGridData" >
<uc:ExtendedGridControl.Columns>
<dxg::GridColumn FieldName="FieldA" Visible="True" />
...
</uc:ExtendedGridControl.Columns>
</uc:ExtendedGridControl>
</Grid
</Window>
How can I expose the GridControl properties? Any help/suggestions would be greatly appreciated.
You need to propagate the properties by defining them on the UserControl, e.g.
public partial class Bogus : UserControl
{
// You often can reuse properties via DependencyProperty.AddOwner
public static readonly DependencyProperty ItemsSourceProperty = ItemsControl.ItemsSourceProperty.AddOwner(typeof(Bogus));
public IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly DependencyProperty ItemTemplateProperty = ItemsControl.ItemTemplateProperty.AddOwner(typeof(Bogus));
public DataTemplate ItemTemplate
{
get { return (DataTemplate)GetValue(ItemTemplateProperty); }
set { SetValue(ItemTemplateProperty, value); }
}
public Bogus()
{
InitializeComponent();
}
}
<UserControl x:Class="Test.UserControls.Bogus" 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" Name="control">
<StackPanel>
<TextBlock Text="Lorem Ipsum:" />
<ItemsControl ItemsSource="{Binding ElementName=control, Path=ItemsSource}"
ItemTemplate="{Binding ElementName=control, Path=ItemTemplate}" />
</StackPanel>
</UserControl>
The properties are visible outside and the internal controls bind to them.
For some properties you do not use a DependencyProperty, but just a clr-property which references the internal control's property, this may be preferable with certain properties that only have setters or internal constructors or are not dependency properties in the internal controls either, e.g.
public ItemCollection Items
{
get { return _itemsControl.Items; }
}

Resources