Selecteddate not updated in wpf datepicker when datepicker contains plain wpf textbox - c#

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.

Related

Setting StringFormat of WPF DatePicker inside UserControl

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.

WPF datagrid how to get access to the row click

Seems that a WPF application I inherited has a DataGrid
<DataGrid x:Name="dataGrid" ItemsSource="{Binding AllTroubleCalls}" SelectedIndex="{Binding SelectedIndex}"
I want to end up setting the Background color to yellow only if the Textbox contains text in it.
The text appears when I click on certain rows in the Datagrid
It seems that everything is based upon this "Binding"
{Binding ... }
I have a textbox that I added a Name to it
<TextBox ToolTipService.ShowDuration="120000" ToolTip="{Binding ThreatText}" Name="txtThreat" Text="{Binding ThreatText}"
TextWrapping="Wrap" AcceptsReturn="True"
VerticalScrollBarVisibility="Visible" HorizontalScrollBarVisibility="Visible"
Margin="3" Grid.Row="8" Grid.Column="1"
Grid.ColumnSpan="3" Grid.RowSpan="1" IsReadOnly="True" Height="30"/>
Show then when I am in a method I can "test" this and it works
txtThreat.Background = new SolidColorBrush(Colors.Yellow);
However, I'm not understanding why I cannot get a handle on the data changing from whatever row is clicked on in the Datagrid, that data then appears "magically" in many textboxes etc.. on the xaml page.
I gather that the "Binding" is handling this, but it is also MVVM 2 way binding ?
I have tried plastering so many breakpoints into so many methods but I can't seem to get any of them to show me how the data is changing on row click
There's a little too much going on in this question without the details. However, I can at least answer what I think is your goal. The easiest way to get a textbox to be yellow if there's a text in it is with a style:
<TextBox >
<TextBox.Style>
<Style TargetType="TextBox">
<Setter Property="Background" Value="Yellow"></Setter>
<Style.Triggers>
<Trigger Property="Text" Value="">
<Setter Property="Background" Value="White"></Setter>
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
As for figuring out how the data is changing... The Text displayed in your TextBox is bound to ThreatText. That means the text should change only when a property named ThreatText is changed in a class somewhere (your viewmodel). Have you tried putting a breakpoint on the setter for the ThreatText property? You can also put a breakpoint on the getter as well. It sounds like that when you click in the textbox/row, WPF updates the text in the UI, which means it's reevaluating the binding due to some change in ThreatText; this also means it'll hit the getter... you can check out the stack trace if it does to see what's going on.

Window with ListBox is not displaying error on the ListBox

I have a dialog in my WPF application which contains a ListBox. The ListBox uses the following DataTemplate to display its contents:
<DataTemplate x:Key="AlarmClassTemplate">
<CheckBox Content="{Binding Path=Value}"
IsChecked="{Binding IsChecked}" />
</DataTemplate>
I've also configured the following template and style to display when there is an error in the ListBox's contents:
<ControlTemplate x:Key="InputErrorTemplateA">
<DockPanel LastChildFill="True">
<Image DockPanel.Dock="Right"
Height="30"
Margin="5"
Source="{StaticResource ErrorImage}"
ToolTip="Contains invalid data"
VerticalAlignment="Center"
Width="30" />
<Border BorderBrush="Red"
BorderThickness="5"
Margin="5">
<AdornedElementPlaceholder />
</Border>
</DockPanel>
</ControlTemplate>
<Style TargetType="{x:Type ListBox}">
<Setter Property="Validation.ErrorTemplate" Value="{StaticResource InputErrorTemplateA}" />
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="ToolTip">
<Setter.Value>
<Binding Path="(Validation.Errors).CurrentItem.ErrorContent" RelativeSource="{x:Static RelativeSource.Self}" />
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
And here's the XAML for the ListBox itself:
<ListBox FontSize="20"
FontWeight="Bold"
Grid.Column="1"
Grid.ColumnSpan="2"
Grid.Row="1"
Height="158"
ItemsSource="{Binding Path=IDs, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"
ItemTemplate="{StaticResource AlarmClassTemplate}"
Margin="5,0,110,0"
Name="AlarmClassListBox"
ToolTip="{x:Static res:Car.EditDataRetention_AlarmClasses_ToolTip}"
Visibility="{Binding Converter={StaticResource BoolToVisibility}, Path=DataTypeIsAlarms}" />
The validation logic for the data in the ListBox is that at least one item has to be checked off. If none of them are, the ListBox should display an error and the OK button on the dialog should be disabled.
The good news is that the OK button on the dialog is indeed disabled when nothing in the ListBox is checked. The bad news is that the Style doesn't seem to be working, in that no red border is displayed around the ListBox and the error image (a red circle with a white exclamation point inside) does not show.
I'm using the same exact ControlTempate and a similar Style on other controls on the same dialog and they work fine. What am I doing wrong? Is it the ListBox? Does ListBox validation work differently?
Indeed the problem is you weren't raising PropertyChanged event for your validation to gets fired.
But i can see one more issue in your code. You have set local value for tooltip on ListBox here:
ToolTip="{x:Static res:Car.EditDataRetention_AlarmClasses_ToolTip}"
But you want different tooltip in case validation returns some error which you define in style triggers.
But, local value has higher precedence order than style triggers. So, your tooltip will never be set. So, you should move the tooltip to style setters to work:
<Setter Property="ToolTip"
Value="{x:Static res:Car.EditDataRetention_AlarmClasses_ToolTip}"/>
MSDN link - Dependency property value precedence.
I found the answer to my problem in this post. It turns out that I have to raise the PropertyChanged event when the checkboxes change in order for the Validation logic to fire. Since the items in the ListBox implement INotifyPropertyChanged, it was easy to add an event listener for each item as it's added to the ListBox which raises the necessary event.
Thanks anyway.

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>

WPF: Row of ToggleButtons with equal length?

I'm currently displaying RadioButtons in my application as rows of ToggleButtons (see my last question). However, I'd like the buttons to be of the same width - currently, every button is just as wide as it has to be.
Since I'm working with templates, I'd like to avoid specifying the width every time I use the control if possible - instead, the width of every button in the row should be equal to that of the widest button in that group.
Any ideas how to do this in XAML? :-)
Maybe using a UniformGrid and setting int the style the property HorizontalAlignement="Stretch" will help.
If you have access to all the toggle buttons (e.g. they aren't databound) then there is a neat trick you can do by binding the minwidth of each button to the width of the one next to it. With the final button being bound to the first:
<StackPanel Orientation="Horizontal">
<Button x:Name="Button1" Content="Long text" MinWidth="{Binding ElementName=Button2, Path=ActualWidth}"/>
<Button x:Name="Button2" Content="A" MinWidth="{Binding ElementName=Button3, Path=ActualWidth}"/>
<Button x:Name="Button3" Content="Extremely long text that should cause this button to be really wide" MinWidth="{Binding ElementName=Button1, Path=ActualWidth}"/>
</StackPanel>
in your resources section create a style that targets ToggleButtons and sets the width to whatever value you want.
<Window.Resources>
<Style TargetType="{x:Type ToggleButton}">
<Setter Property="Width" Value="50" />
</Style>
</Window.Resources>
Use a grid with a 1 button in each column and definethe widths like this

Categories

Resources