In my WPF appp, I created a custom control by subclassing TextBox. I then added a DependencyProperty called BorderWhenRequired (the border to use when the TextBox represents a required field), like so:
public class TextBoxEx : TextBox
{
public static readonly DependencyProperty BorderWhenRequiredProperty = DependencyProperty.Register(
"BorderWhenRequired", typeof(Brush), typeof(TextBoxEx),
new FrameworkPropertyMetadata(default(Brush), FrameworkPropertyMetadataOptions.AffectsRender)
);
public Brush BorderWhenRequired
{
get { return (Brush)GetValue(BorderWhenRequiredProperty); }
set { SetValue(BorderWhenRequiredProperty, value); }
}
}
I then created a resource that holds the brush value for the border, and a style for the control:
<SolidColorBrush x:Key="RequiredControlBorderBrush">Purple</SolidColorBrush>
<Style TargetType="{x:Type implementations:TextBoxEx}">
<Setter Property="BorderWhenRequired" Value="{StaticResource RequiredControlBorderBrush}" />
</Style>
but the designer is showing me:
ArgumentException: '#FF800080' is not a valid value for the 'MyNs.Common.Controls.Implementations.TextBoxEx.BorderWhenRequired' property on a Setter.
I tried defining the resource RequiredControlBorderBrush as a Brush, Color and SolidColorBrush, but I keep getting the same error.
Solved my own problem. In case anyone comes across this: I made a careless mistake. All Color properties in my custom class where defined as System.Drawing.Brush, when they should have been System.Window.Media.Brush.
Related
In Wpf I inherit from the Button class:
public class MenuButton : Button
{
public MenuButton()
{
this.Style = FindResource("MenuButton") as Style;
}
public MenuButton(string caption, ICommand command)
: this()
{
SetValue(ContentProperty, caption);
this.command = command;
}
}
In a window I added a control
<c:MenuButtons x:Name="MenuProject" c:MenuItems="{x:Static d:MenuItems.Project}" />
Where c: en d: are my namespaces.
MenuItems is a dependencyproperty of usercontrol MenuButtons and is declared in a class like:
public readonly static MenuButton[] Project = new MenuButton[] { new MenuButton("Open", new Command()), ..etc };
The resource is defined like:
<Style x:Key="MenuButton"
BasedOn="{StaticResource {x:Static ToolBar.ButtonStyleKey}}"
TargetType="{x:Type c:MenuButton}">
The visual studio designer says: 'MenuButton' TargetType does not match type of element 'MenuButton'. The code runs fine, but the designer has trouble invoking the constructor of my MenuButton class. When commenting out that line the error disappears, but then the style is not applied and vice versa.
this.Style = FindResource("MenuButton") as Style; should not appear in the constructor.
You can apply it in the OnApplyTemplate method
public overrride OnApplyTemplate()
{
this.Style = FindResource("MenuButton") as Style;
base.OnApplyTemplate();
}
Hope this helps
In my application i need to change the properties related to a datagrid header like ColumnHeader Font, fontsize etc. As there is no single property for the same currently, i am updating this through Style setters. But the problem is for a single property change(like FontSize) i have to create an entire collection of the SetterBase and update the single property along with the other propertied in the setterbase collection. Is there any other way to update a property as in this scenario.
Code snippet:
set
{
Style m_ColumnHeaderStyle = new Style(typeof(DataGridColumnHeader));
m_ColumnHeaderStyle.Setters.Add(m_ColumnFontWeightProperty);
m_ColumnHeaderStyle.Setters.Add(m_ColumnFontSizeProperty);
m_ColumnHeaderStyle.Setters.Add(m_ColumnFontItalicProperty);
m_ColumnFont = new Setter(DataGridColumnHeader.FontFamilyProperty, new FontFamily(value));
m_ColumnHeaderStyle.Setters.Add(m_ColumnFont);
this.MyDataGrid.ColumnHeaderStyle = m_ColumnHeaderStyle;
}
Styles in wpf have the ability to update attached values so you can declare styles in xaml once:
<DataGrid >
<DataGrid.ColumnHeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="FontFamily" Value="{Binding HeaderFont}"/>
</Style>
</DataGrid.ColumnHeaderStyle>
</DataGrid>
The magic happens in Binding and there are few kinds of it.
public partial class MainWindow : Window, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private FontFamily _headerFont;
public FontFamily HeaderFont
{
get
{
return _headerFont;
}
set
{
_headerFont = value;
PropertyChanged(this, new PropertyChangedEventArgs("HeaderFont"));
}
}
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
}
It is imperative that PropertyChanged event is fired when property changes.
I am trying to validate my model class using IDataErrorInfo as given below.
//Validators
public string this[string propertyName] {
get {
string error = null;
if (propertyName == "Name") {
error = ValidateName();
}
return error;
}
}
This works fine, except that when the view first loads it already contains validation errors. Is it possible to ignore/suppress validation errors when the view is first loaded. Also, is it a common practice to show errors when the view loads and before the User starts the data entry for the model properties.
regards,
Nirvan.
Edit:
This is how I am setting up IDataErrorInfo.
<TextBox Text="{Binding Name, ValidatesOnDataErrors=True}" Grid.Row="1" Grid.Column="1" />
I have took the following approach and it works. Basically, the Model should rightfully record errors and have them listed up in a dictionary, even if the object is just instantiated and User hasn't entered any Text yet. So I didn't change my Model code or the IDataErrorInfo validation code. Instead, I just set the Validation.Error Template property to {x:Null} initially. Then there is code to wire up the TextBox's LostFocus event that changes the Validation.Error template back to what I am using. In order to achieve the swapping of templates and attaching LostFocus event handler to all TextBox's in my application, I used a couple of dependency properties. Here is the code that I have used.
Dependency Properties and LostFocus Code:
public static DependencyProperty IsDirtyEnabledProperty = DependencyProperty.RegisterAttached("IsDirtyEnabled",
typeof(bool), typeof(TextBoxExtensions), new PropertyMetadata(false, OnIsDirtyEnabledChanged));
public static bool GetIsDirtyEnabled(TextBox target) {return (bool)target.GetValue(IsDirtyEnabledProperty);}
public static void SetIsDirtyEnabled(TextBox target, bool value) {target.SetValue(IsDirtyEnabledProperty, value);}
public static DependencyProperty ShowErrorTemplateProperty = DependencyProperty.RegisterAttached("ShowErrorTemplate",
typeof(bool), typeof(TextBoxExtensions), new PropertyMetadata(false));
public static bool GetShowErrorTemplate(TextBox target) { return (bool)target.GetValue(ShowErrorTemplateProperty); }
public static void SetShowErrorTemplate(TextBox target, bool value) { target.SetValue(ShowErrorTemplateProperty, value); }
private static void OnIsDirtyEnabledChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args) {
TextBox textBox = (TextBox)dependencyObject;
if (textBox != null) {
textBox.LostFocus += (s, e) => {
if ((bool) textBox.GetValue(ShowErrorTemplateProperty) == false) {
textBox.SetValue(ShowErrorTemplateProperty, true);
}
};
}
}
If IsDirtyEnabled dependency property is set to true, it uses the callback to attach the TextBox's LostFocus event to a handler. The handler just changes the ShowErrorTemplate attached property to true, which in turn triggers in textbox's style trigger to show the Validation.Error template, when the TextBox loses focus.
TextBox Styles:
<Style TargetType="{x:Type TextBox}">
<Setter Property="Validation.ErrorTemplate" Value="{StaticResource ValidationErrorTemplate}"/>
<Setter Property="gs:TextBoxExtensions.IsDirtyEnabled" Value="True" />
<Style.Triggers>
<Trigger Property="gs:TextBoxExtensions.ShowErrorTemplate" Value="false">
<Setter Property="Validation.ErrorTemplate" Value="{x:Null}"/>
</Trigger>
</Style.Triggers>
</Style>
This may seem too much of code for a simple thing, but then I have to do it only once for all the Textboxes I am using.
regards,
Nirvan.
try to set the data context AFTER the view has been shown.
In my case, this helped.
Are you throwing the exception in the get?
public string Name
{
get { return _name; }
set
{
_name = value;
if (String.IsNullOrEmpty(value))
{
throw new ApplicationException("Customer name is mandatory.");
}
}
}
you set the rules in your ValidateName() method. your view just show the error :) i suggest that name is a mandatory field and should be filled by the user but you dont like the red border when the view is first loaded?
i use two different control templates for validation.errortemplate the normal one and one for mandatory fields (a red *)
thats the way i did it the last time.
In WPF, is it possible to bind the key in "{StaticResource key}"to a variable.
For example. I have a variable ExecutionState with the states Active and Completed.
In my ResourceDictionary I have
<Style TargetType="{x:Type TextBlock}" x:Key="Active">
<Setter Property="Foreground" Value="Yellow"/>
</Style>
<Style TargetType="{x:Type TextBlock}" x:Key="Completed">
<Setter Property="Foreground" Value="Green"/>
</Style>
Instead of having
<TextBlock Style="{StaticResource Active}"/>
I Would like to have something like
<TextBlock Style="{StaticResource {Binding ExecutionState}}"/>
Thus if the state changes the text color changes.
Is something like this even possible?
I can achieve the wanted functionality using triggers, but I have to reuse it at several places and I don't want to clutter my code.
I am using MVVM also.
thanx
No, it's not possible. Binding can only be set on a DependencyProperty. StaticResource is not a DependencyObject, so there is no DependencyProperty. You should use Trigger(s) or develop your own attached behavior.
There is no direct way to achieve .
Create one attached property and assign the property name to bind.
In the property change callback function update control style.
<TextBlock dep:CustomStyle.StyleName="{Binding ExecutionState}" Text="Thiru" />
public static class CustomStyle
{
static FrameworkPropertyMetadata _styleMetadata = new FrameworkPropertyMetadata(
string.Empty, FrameworkPropertyMetadataOptions.AffectsRender, StyleNamePropertyChangeCallBack);
public static readonly DependencyProperty StyleNameProperty =
DependencyProperty.RegisterAttached("StyleName", typeof (String), typeof (CustomStyle), _styleMetadata);
public static void SetStyleName(UIElement element, string value)
{
element.SetValue(StyleNameProperty, value);
}
public static Boolean GetStyleName(UIElement element)
{
return (Boolean)element.GetValue(StyleNameProperty);
}
public static void StyleNamePropertyChangeCallBack(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
FrameworkElement ctrl = d as FrameworkElement;
if (ctrl.IsLoaded)
{
string styleName = Convert.ToString(e.NewValue);
if (!string.IsNullOrEmpty(styleName))
{
ctrl.Style = ctrl.TryFindResource(styleName) as Style;
}
}
}
}
I have a usercontrol that expose a public property like this :
public Double ButtonImageHeight
{
get { return imgButtonImage.Height; }
set { imgButtonImage.Height = value; }
}
when I use that control, I want to be able to set that property throught a Style like that :
<Style x:Key="MyButtonStyle" TargetType="my:CustomButtonUserControl" >
<Setter Property="ButtonImageHeight" Value="100" />
</Style>
What am I doing wrong?
Thanks
thanks Matt, I just found it myself but you were absolutely right... here's the exact code I used in case it can help someone else (all the examples I found were on WPF, silverlight is just slightly different) :
public static readonly DependencyProperty ButtonImageHeightProperty = DependencyProperty.Register("ButtonImageHeight", typeof(Double), typeof(CustomButtonUserControl),new PropertyMetadata(ButtonImageHeight_PropertyChanged ));
public Double ButtonImageHeight
{
get { return (Double)GetValue(ButtonImageHeightProperty); }
set { SetValue(ButtonImageHeightProperty, value); }
}
private static void ButtonImageHeight_PropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
((CustomButtonUserControl)source).imgButtonImage.Height = (Double)e.NewValue;
}
The property needs to be a dependency property in order to support styles.
You can make it even more generic and nice by passing through a Style for your imgButtonImage, that way you can set multiple properties. So within your user control add the dependency property, but make it a Style:
public static readonly DependencyProperty UseStyleProperty =
DependencyProperty.Register("UseStyle", typeof(Style), typeof(CustomButtonUserControl), new PropertyMetadata(UseStyle_PropertyChanged));
public Style UseStyle
{
get { return (Style)GetValue(UseStyleProperty); }
set { SetValue(UseStyleProperty, value); }
}
private static void UseStyle_PropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
((CustomButtonUserControl)source).imgButtonImage.Style = (Style)e.NewValue;
}
Notice how within the PropertyChanged function I set the style of the control to the new style.
Then when I host the UserControl I can pass through the style:
<Style x:Name="MyFancyStyle" TargetType="Button" >
<Setter Property="FontSize" Value="24" />
</Style>
<controls:MyUserControl UseStyle="{StaticResource MyFancyStyle}" />
works in VS design mode too! (It's a miracle)