Setting StringFormat of WPF DatePicker inside UserControl - c#

I have a custom WPF UserControl that uses a DatePicker within it. I'm setting the display format of the DatePicker using the answer provided at this SO article
<Style TargetType="{x:Type DatePickerTextBox}" BasedOn="{StaticResource {x:Type DatePickerTextBox}}">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<TextBox x:Name="PART_TextBox"
Text="{Binding Path=SelectedDate, StringFormat='dd-MM-yy', RelativeSource={RelativeSource AncestorType={x:Type DatePicker}}}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I would like to use a different format string for different instances of the control, so I'd like in some way to provide the format when I add the UserControl to the form, something like
<basecontrols:CustomControl
LabelWidth="{StaticResource LabelColumnWidth}"
Label="Custom Information"
DateDisplayFormat="dd-MMMM-yyyy"
/>
Label and LabelWidth are Dependancy properties of the custom UserControl.
Is it possible to have bind the StringFormat to a control property, when it is inside a Binding ? If not, is there a way to do what I want to do?
Hope that makes sense

Is it possible to have bind the StringFormat to a control property, when it is inside a Binding ?
No. You can't bind the StringFormat property of a Binding because it's not a dependency property.
What you could to is to define a DateDisplayFormat dependency property in your CustomControl (which I guess you have done already) and then override the OnApplyTemplate method and create the binding of the TextBox programmatically.
Alternatively, you could use a <MultiBinding> in the XAML markup that binds to both SelectedDate and DateDisplayFormat and use a multi converter that returns a string.

Related

Validating UserControl's control by Window's ViewModel

Suppose, I have created custom LoginForm as an UserControl, which consists of TextBox and PasswordBox. TextBox has installed custom ErrorTemplate. Naturally, I would like to have this LoginForm as reusable as can, therefore I want to separate validation logic from this LoginForm.
The problem is, that if I bind LoginForm's text property to the "validation-property" of the ViewModel : IDataErrorInfo, that is set as Window's DataContext, the ErrorTemplate is not being applied to LoginForm's TextBox even if I see debug logs from ViewModel's validator.
How can I validate child controls of reusable component via independent ViewModel?
use this error template in application resource:
<Style TargetType="{x:Type TextBox}">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<DockPanel LastChildFill="True">
<Border BorderBrush="Red" BorderThickness="0.8">
<AdornedElementPlaceholder Name="adornerPlaceholder"></AdornedElementPlaceholder>
</Border>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={RelativeSource Self},
Path=(Validation.Errors)[0].ErrorContent}" />
</Trigger>
</Style.Triggers>
</Style>
After hours of struggling how to solve this, I came up with following solution which satisfies my requirements and so MVVM pattern:
Create in UserControl a DepdendencyProperty of type IDataErrorInfo which will be later implemented by your ViewModel (for my purposes, I used ISignUpValidator:IDataErrorInfo with UsernameValue property).
Lets say this property is registered under the name Validator (as default value I used "do-nothing" implementation of my interface).
Bind UserControl's TextBox.Text property to Validator property:
Lets say TextBox.Text property is exposed to UserControl under the name
Username:
Username="{Binding Path=Validator.UsernameValue, ElementName=UserControlName,
UpdateSourceTrigger=PropertyChanged,
ValidatesOnDataErrors=True}"
Finally Bind your Viewmodel to your UserControl's Validator property
<Window.Resources>
<local:ViewModel x:Key="ViewModel"/>
<Window.Resources>
<local:LoginForm Validator={StaticResource ViewModel}>
Or alternatively, if your ViewModel is already set as Window's DataContext:
<local:LoginForm Validator="{Binding DataContext, ElementName=WindowName}"}>

WPF ControlTemplate AND DataTemplate

I have ListView where I would like to apply a custom ControlTemplate to it's items. It is defined like this:
<ListView ItemsSource="{Binding MyAwesomeItems}" ...
MyAwesomeItems holds different classes. So I thought to myself: "Well, hello DataTemplates."
To make the contained items look the way I want them to, I have defined a ControlTemplate like this:
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<Border><ContentControl Content="{TemplateBinding Content}"/></Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
I have used ContentControl with Binding to TemplateBinding Content. I expected that WPF would then insert my items inside that ContentControl, using whatever DataTemplate I have defined for it.
But instead, it looks like WPF just uses the items .ToString() and does not apply any DataTemplates. Is this intended behaviour?
What I want to achieve is: Have a list of items, where the container of each item looks exactly the way I want and the content of that container comes from the DataTemplate.
In a ControlTemplate for a ContentControl you usually use an empty ContentPresenter tag. In your case:
<ControlTemplate TargetType="ListViewItem">
<Border>
<ContentPresenter/>
</Border>
</ControlTemplate>
The ContentPresenter has a ContentSource property which defaults to "Content" and sets all the necessary properties (Content, ContentTemplate, etc.).
See here for details.

Selecteddate not updated in wpf datepicker when datepicker contains plain wpf textbox

I have extended a wpf date picker control and in styles I changed the template for date picker textbox style. I put a textbox in the control template of the datepickertextbox:
<Style x:Key="DatePickerTextBoxStyle" TargetType="{x:Type DatePickerTextBox}">
<Setter Property="Height" Value="20" />
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<TextBox x:Name="PART_TextBox"
Style="{DynamicResource CalendarTextBoxStyle}"
TabIndex="0"
Text="{Binding Path=SelectedDate,
StringFormat='d',
ConverterCulture={x:Static glob:CultureInfo.CurrentCulture},
RelativeSource={RelativeSource AncestorType={x:Type maskedDatePickerLib:MaskedDatePicker}}}"
TextWrapping="Wrap">
</TextBox>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I have overridden the default template for date picker control only style for datepicker textbox is changed.
Now, the problem is when I select a date through calender it got displayed in text box through binding. but when I delete the date through backspace and if I try to select the same date again from calender, It does not get displayed in text box. When I investigated through snoop I saw the in SelectedDate property of DatePicker control the value I deleted is still there but in text box text propery value is empty as I deleted it. Kindly suggest.
If you look at the default ControlTemplate for the DatePicker control, you will see that it has four inner controls that have names that start with the PART_ prefix. (You can find the default ControlTemplate for the DatePicker control in the DatePicker Syles and Templates page on MSDN). Here is an example from the linked page:
<Button x:Name="PART_Button"
Grid.Column="1"
Foreground="{TemplateBinding Foreground}"
Focusable="False"
HorizontalAlignment="Left"
Margin="3,0,3,0"
Grid.Row="0"
Style="{StaticResource DropDownButtonStyle}"
VerticalAlignment="Top" />
The controls that have names that start with the PART_ prefix are internally used by the DatePicker class and so not having them in the new ControlTemplate will cause problems. The class asks for the controls by name and if they are not found, then default functionality cannot be completed.
Furthermore, you are trying to replace a DatePickerTextBox with all of its built in functionality for a normal TextBox and then wondering why the default functionality is not working properly... it's because you removed it and didn't replace it.
While I can see that you have tried to use the PART_ name it is clear that you don't fully understand what it does. The PART_TextBox control in the default ControlTemplate is of type DatePickerTextBox, but yours is of type TextBox, so whatever the DatePicker class normally does with that control, it now cannot do because it is of the wrong type.

Binding Button Tooltip with Content / Label

I have this code:
<ribbon:Button Label="Hello" />
I wanted to bind its tooltip to Label like:
<ribbon:Button Label="Hello" ToolTip="Hello" />
I have already tried creating style with the following info, but failed:
<Style TargetType="{x:Type ribbon:Button}">
<Setter Property="ToolTip" Value="{Binding Text}" />
</Style>
Please tell me how can I fix this. Instead of ribbon:Button, normal button code be used and I want to bind its tooltip property with its Content property.
Edit: One more thing after trying, if i set the Value property of Setter inside style without any binding, it works fine. Eg:
<Style TargetType="{x:Type ribbon:Button}">
<Setter Property="ToolTip" Value="This will show" />
</Style>
But binding is not applied here. So problem arises only when binding is done :(
Of course your style won't work, because you always bind to the properties of your DataContext, which i doubt is your Control itself.
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=Text}"/>
should work fine.
The golden rule for binding is: look for binding errors in the debug output.
Secondly, you are binding Text, where everywhere else you use the property label.
Thirdly, understand that the basis for Binding is the DataContext. You can give Xaml elements a name, then use the Binding syntax based on ElementName to bind to FrameworkElement properties.

WPF listbox. Skip underscore symbols in strings

I have some WPF ListBox which is dynamically populating with items. Something like this :
ListBox.Items.Add
(new ListBoxItem { Content = new CheckBox { IsChecked = true, Content = "string_string"} );
The problem is with checkbox content. It's showing on GUI like "stringstring"...
How to escape the "_" symbols ? (I get strings dynamically)
You can add the text in a TextBlock and put that TextBlock inside your Chekbox, TextBlock does not support _ mnemonic characters. Here's what I mean, in xaml, but you can easily convert this to code:
<CheckBox IsChecked="True">
<TextBlock>string_string</TextBlock>
</CheckBox>
The default template for the CheckBox contains a ContentPresenter whose RecognizesAccessKey is set to true. If the content is a string (which it is in your case), then the ContentPresenter creates an AccessText element to display the text. That element hides the underscore until the Alt key is pressed because it will treat it as a mnemonic. You can either retemplate the CheckBox such that its ContentPresenter's RecognizesAccessKey is false or better yet just provide a DataTemplate as the ContentTemplate which contains a TextBlock. If you're not sure if the content will be a string then you can set the ContentTemplateSelector and in code provide a DataTemplate which contains a TextBlock only if the item is a string. e.g.
<ListBox xmlns:sys="clr-namespace:System;assembly=mscorlib">
<ListBox.Resources>
<DataTemplate DataType="sys:String" x:Key="stringTemplate">
<TextBlock Text="{Binding}" />
</DataTemplate>
<Style TargetType="CheckBox">
<Setter Property="ContentTemplate" Value="{StaticResource stringTemplate}" />
</Style>
</ListBox.Resources>
<ListBoxItem>
<CheckBox Content="A_B" ContentTemplate="{StaticResource stringTemplate}"/>
<!-- Or use the implicit style to set the ContentTemplate -->
<CheckBox Content="A_B" />
</ListBoxItem>
</ListBox>
Use a double underscore string__string, since in WPF, the _ is the mnemonic character.
Even better, just solve this issue in xaml and create a collection in your view model (or code-behind).
I had the same problem in a DataGrid. Similarly to AndrewS, I added a style for TextBlock, but did not use DataTemplate or ContentTemplate. This way the setters in ColumnHeaderStyle were applied :) However, this solution works only for single underscores, eg. "a_b", but not for "a__b".
<DataGrid>
<DataGrid.Resources>
<Style TargetType="TextBlock">
<Setter Property="Text" Value="{Binding}"/>
</Style>
</DataGrid.Resources>
<DataGrid.ColumnHeaderStyle>
<!-- my setters here do not get overridden -->
</DataGrid.ColumnHeaderStyle>
</DataGrid>

Categories

Resources