I have a datepicker in my C# 4.0 (WPF) application and I would like to change the format of the date that is visible in the textBox to yyyy/MM/dd. Now I see the format dd/MM/yyyy.
In my axml of the datePicker I have this code:
<DatePicker Height="25" HorizontalAlignment="Left" Margin="5,36,0,0" Name="dtpStartDate"
SelectedDate="{Binding StartDateSelectedDate}" VerticalAlignment="Top" Width="115">
<DatePicker.Resources>
<Style TargetType="{x:Type DatePickerTextBox}">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<TextBox x:Name="PART_TextBox"
Text="{Binding Path=SelectedDate, RelativeSource={RelativeSource AncestorType={x:Type DatePicker}}, StringFormat={}{0:yyyy/MM/dd}}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</DatePicker.Resources>
</DatePicker>
This seems in a first time that all works fine, I can see the date in the format that I want, and I can change the date manually or using the calendar, and in both ways the date that arrives to the viewModel is the correct.
But I have a problem, because I would like to detect that if the date is empty, in my view model control this case. But If I clear the datepicker, in my view model arrives the last correct date, so I can't check if the date is empty or not.
So how can I modify the format of the date in the date picker and control if the date is empty/null or not?
Thanks.
Daimroc.
you can try the following solution.
First create the following converter :
public class StringToDateTimeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null)
{
return null;
}
return ((DateTime)value).ToString(parameter as string, CultureInfo.InvariantCulture);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (string.IsNullOrEmpty(value as string))
{
return null;
}
try
{
DateTime dt = DateTime.ParseExact(value as string, parameter as string, CultureInfo.InvariantCulture);
return dt as DateTime?;
}
catch (Exception)
{
return null;
}
}
}
Then in the xaml, you will have to create an instance of the converter and use it in the textbox of the DatePicker
<Window x:Class="TestDatePicker.MainWindow"
...
xmlns:converters="clr-namespace:TestDatePicker"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
...
<converters:StringToDateTimeConverter x:Key="StringToDateTimeConverter" />
</Window.Resources>
<Grid DataContext="{StaticResource MainWindowVM}">
...
<DatePicker Height="25" HorizontalAlignment="Left" Margin="5,36,0,0" Name="dtpStartDate"
SelectedDate="{Binding StartDateSelectedDate}" VerticalAlignment="Top" Width="115">
<DatePicker.Resources>
<Style TargetType="{x:Type DatePickerTextBox}">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<TextBox x:Name="PART_TextBox"
Text="{Binding Path=SelectedDate, RelativeSource={RelativeSource AncestorType={x:Type DatePicker}}, Converter={StaticResource StringToDateTimeConverter}, ConverterParameter=yyyy/MM/dd}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</DatePicker.Resources>
</DatePicker>
...
</Grid>
Finally, in the viewmodel, the property must be of type DateTime? (i.e a nullable DateTime).
private DateTime? _startDateSelectedDate;
public DateTime? StartDateSelectedDate
{
get { return _startDateSelectedDate; }
set
{
if (_startDateSelectedDate != value)
{
_startDateSelectedDate = value;
RaisePropertyChanged(() => this.StartDateSelectedDate);
}
}
}
I hope this will help you
Regards
Claude
defaultly the DateTimerPicker does not support null values.
Maybe this post from MSDN with the same topic can help you.
There you will find other ideas how to implement it or some code project for nullable date time picker.
Related
I am creating a wpf app and struggling with this problem for sometime now.
I have a datagrid with DataGridTemplateColumn, which contains a checkbox and textblock.
<DataGrid
Name="ChargeDataGrid"
Grid.Row="1"
AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox x:Name="CheckBox1"/>
<TextBlock Text="{Binding}" />
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox />
<TextBlock Text="Title" />
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
<system:String>123</system:String>
<system:String>124</system:String>
<system:String>125</system:String>
<system:String>126</system:String>
<system:String>127</system:String>
</DataGrid>
What i need to achieve is when row is clicked checkbox in this row must be in checked state too.
I tried to use style triggers:
<Style TargetType="{x:Type DataGridCell}">
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="CheckBox1.IsChecked" Value="True" />
<Setter Property="Background" Value="Blue" />
</Trigger>
</Style.Triggers>
</Style>
but it didn't seem possible to change checkbox state like this. I know how to do it in code-behind or mvvm style, but in this case i am wondering is it possible to do using xaml only?
Any help would be appreciated.
I am afraid but with plain standard XAML you can't do it.
As I see you have two options:
You can use some extension libraries which will expand functionality of bindings. Some functionality can be found in mvvm frameworks like MugenMvvmToolkit
Second option is to use some converter for this purpose.
My solution for the second variant is a kind of hack and to my mind more elegant way would be with code behind. Converter:
public class MultiValueConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length != 3) throw new ArgumentException("Should be 3 params");
if (!(values[2] is FrameworkElement element)) return values[1];
if (!(bool)values[0])
{
element.Tag = "Value need to be changed.";
return values[1];
}
if (element.Tag.Equals("Value changed.")) return values[1];
var res = !(bool)(values[1] ?? true);
element.Tag = "Value changed.";
return res;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Converter inversing bool variable and saves previous state in Tag property of control. This is still not code behind and pretty reusable solution. You can use such converter in any other view where you need such behaviour
In XAML I've changed only checkbox control definition:
<CheckBox x:Name="RowCheckBox" IsHitTestVisible="False">
<CheckBox.IsChecked>
<MultiBinding Converter="{StaticResource MultiValueConverter}" Mode="OneWay">
<Binding Path="IsSelected" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type DataGridRow}}" />
<Binding Path="IsChecked" RelativeSource="{RelativeSource Self}" />
<Binding Mode="OneWay" RelativeSource="{RelativeSource Self}" />
</MultiBinding>
</CheckBox.IsChecked>
</CheckBox>
I think it can bed done rather simple like this:
<CheckBox x:Name="CheckBox1" IsChecked="{Binding RelativeSource={RelativeSource AncestorType=DataGridRow}, Path=IsSelected}"/>
EDIT:
If the desired result is to only change the IsChecked state when (re)selecting the row it can be done with a attached property on a DependencyObject (for instance the containing window) like this:
1) Define the checkbox as this:
<CheckBox x:Name="CheckBox1" IsEnabled="true" local:MainWindow.CheckboxChecked="{Binding RelativeSource={RelativeSource AncestorType=DataGridRow}, Path=IsSelected,Mode=OneWay}">
2) Define the attached property as this:
public static bool GetCheckboxChecked(DependencyObject obj)
{
return (bool)obj.GetValue(CheckboxCheckedProperty);
}
public static void SetCheckboxChecked(DependencyObject obj, bool value)
{
obj.SetValue(CheckboxCheckedProperty, value);
}
public static readonly DependencyProperty CheckboxCheckedProperty =
DependencyProperty.RegisterAttached("CheckboxChecked", typeof(bool), typeof(MainWindow), new PropertyMetadata(false, CheckboxChecked_Changed));
private static void CheckboxChecked_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
CheckBox chk = d as CheckBox;
if (chk != null && chk.Tag == null)
{
bool chkValue = chk.IsChecked.GetValueOrDefault();
bool oldValue = (bool)e.OldValue;
bool newValue = (bool)e.NewValue;
chk.Tag = true; // Just to prevent an infinite loop
chk.IsChecked = !chkValue && !newValue || chkValue && !oldValue && newValue ? false : true;
chk.Tag = null;
}
}
I've got this xaml:
<Grid Background="{Binding Source=Typ, Converter={StaticResource ColorConv}}">
Typ is an Enum, this one:
public enum LogEintragTyp { Debug, Fehler, Debug2, Warnung, Analyse, User }
I've got a Converter that converts that type to a color. Heres a part of it:
[ValueConversion(typeof(LogEintragTyp), typeof(Color))]
public class LogTypToColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (!(value.GetType() == typeof(LogEintragTyp)))
throw new Exception("Wrong type!");
LogEintragTyp sv = (LogEintragTyp)value;
The objects that are bound to this template are ALWAYS "LogEintragTyp" values. However it always throws the exception there.
When I debug this in Visual Studio it says "value" is this:
So it says "value" contains "Typ".
But when I look at the from the debugger on mouseover correctly casted value it says this (at the bottom):
"Debug" which is a value that I am looking for... But when I move the debugger to that line to actually let the program execute the conversion, it says invalid cast exception. Why isnt there a "LogEintragTyp" in the "value" object but the string "Typ" which is the name of the value that I'm actually looking for and the debugger is also seeing but the executing program isnt?
Apparently you want to bind to the Typ property of some object, so your Binding should use Path instead of Source:
<Grid Background="{Binding Path=Typ, Converter={StaticResource ColorConv}}">
Or
<Grid Background="{Binding Typ, Converter={StaticResource ColorConv}}">
As comments have pointed out, your value is a string. Try this:
LogEintragTyp result;
if (Enum.TryParse((string)value, true, out result))
{
return some color based on 'result';
}
return something else;
EDIT
The answer from #Clemens is clearly the right one, given the original intent. But if you want to display hardcoded values from XAML, using Source=some-string is one way to do it, with the above changes to the converter. Not exactly type-safe, though. You can also use the actual enum members, as follows:
public enum LogType { Good, Bad, Ugly }
XAML:
<Window
x:Class="WPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WPF">
<StackPanel>
<StackPanel.Resources>
<local:LogTypeToColorConverter x:Key="Converter" />
<Style TargetType="Rectangle">
<Setter Property="Width" Value="50" />
<Setter Property="Height" Value="20" />
</Style>
</StackPanel.Resources>
<Rectangle Fill="{Binding Source={x:Static local:LogType.Good}, Converter={StaticResource Converter}}" />
<Rectangle Fill="{Binding Source={x:Static local:LogType.Bad}, Converter={StaticResource Converter}}" />
<Rectangle Fill="{Binding Source={x:Static local:LogType.Ugly}, Converter={StaticResource Converter}}" />
</StackPanel>
</Window>
The Convert method:
if (value.GetType() != typeof(LogType))
throw new Exception("Wrong type!");
switch ((LogType)value)
{
case LogType.Good: return Brushes.Green;
case LogType.Bad: return Brushes.Red;
case LogType.Ugly: return Brushes.Orange;
default: return Brushes.Black;
}
...which ends up like this:
I have an application that uses two separate projects. One is for the main executable which contains my ViewModels and the other is to control the theme/style of the application.
In the theme project, I have customized the DataGridColumnHeader's Style to include a CheckBox. Now how do I databind the CheckBoxes to my ViewModel?
My theme xaml
<Style x:Key='PlottableFilteringColumnHeaderStyle' TargetType='{x:Type primitives:DataGridColumnHeader}'>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type primitives:DataGridColumnHeader}">
<Grid>
<themes:DataGridHeaderBorder x:Name='HeaderBorder'>
<Grid x:Name="GridColumnHeader">
<StackPanel x:Name="argStackPanel">
<CheckBox x:Name="argCheckBox" Content="Enable Arg" Style="{DynamicResource ResourceKey=DefaultCheckBox}" />
</StackPanel>
</Grid>
</themes:DataGridHeaderBorder>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I am then using MultiBinding for the argCheckBox
public class HeaderArgConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string headerText = values[0] as string;
if (!String.IsNullOrWhiteSpace(headerText))
{
FrameworkElement targetElement = values[1] as FrameworkElement;
DataGridColumnHeader header = targetElement.TemplatedParent as DataGridColumnHeader;
string columnName = header.DataContext != null ? header.DataContext.ToString() : "";
var argNumber = System.Text.RegularExpressions.Regex.Match(columnName.Split(':')[0], #"\d+$").Value; // use the header text to determine which arg number
Binding binding = new Binding("SelectedViewModel.EnableArg" + argNumber);
binding.Source = Window.DataContextProperty; // This is what I am unsure about
(targetElement as CheckBox).SetBinding(CheckBox.IsCheckedProperty, binding);
}
}
}
I keep getting 'BindingExpression path error: property not found on 'object'' error. Any ideas on how to fix this or if there is a better way to do this?
I'm doing a budgeting module.
I will like to ask that how to set the GridViewColumn to display out my desired image based on the value which I retrieve from database which are "income" & "expenses". I know how to retrieve the value from the database & display in the lisview but my question for today is that I will like to have some condition which is when found "income" will populate with income image then found expense will populate with another image???
Can this be possible. Hope to receive reply as soon as possible. Thank you.
I will provide my codes for better refer:
XAML file:
<DataTemplate x:Key="CategoriesType">
<Border BorderBrush="#FF000000" BorderThickness="1,1,0,1" Margin="-6,-2,-6,-2">
<StackPanel Margin="6,2,6,2">
<TextBlock Text="{Binding Path=CategoriesType}"/>
</StackPanel>
</Border>
</DataTemplate>
<Style x:Key="MyItemContainerStyle" TargetType="{x:Type ListViewItem}">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Stretch" />
<!--<EventSetter Event="PreviewMouseLeftButtonDown" Handler="ListViewItem_PreviewMouseLeftButtonDown" />-->
</Style>
</Window.Resources>
<ListView Height="320" HorizontalAlignment="Left" Margin="12,154,0,0" Name="CategoriesListView" VerticalAlignment="Top" Width="316" ItemsSource="{Binding}" ItemContainerStyle="{DynamicResource MyItemContainerStyle}">
<ListView.View>
<GridView>
<GridViewColumn Header="Types" Width="40" CellTemplate="{DynamicResource CategoriesType}"/>
</GridView>
</ListView.View>
</ListView>
Add an image tag in the tamplate and use a converter to return the correct image based on the string value (untested code)
xaml:
<UserControl.Resources>
<Converters:TypeToImageConverter x:Key="typeToImageConverter" />
</UserControl.Resources>
<StackPanel Margin="6,2,6,2">
<Image Source="{Binding Path=CategoriesType,Converter={StaticResource typeToImageConverter}"/>
<TextBlock Text="{Binding Path=CategoriesType}"/>
</StackPanel>
TypeToImageConverter.cs:
public class TypeToImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null)
throw ...
var str = value.ToString();
if (str == "income")
return new BitmapImage(...);
if (str = "expenses")
return new BitmapImage(...);
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
I found the solution for my answer which is the format of the string. Which causing me to unavailable to retrieve the image with few hours of troubleshooting & debugging. I finally found out the solution :)
I solve it by like this:In my converter.cs
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
string str = (string)value;
string newString = str.TrimEnd();//Newly added compared with the old version
if (newString == "income")
return new BitmapImage(new Uri("pack://application:,,,/images/add.png"));
if (newString == "Expenses")
{
//return new BitmapImage(new Uri(#"pack://application:,,,/HouseWivesSavior;component/images/add.png"));
return new BitmapImage(new Uri("pack://application:,,,/images/edit.png"));
}
return null;
}
From referring the above that you can see that I added this code: "string newString = str.TrimEnd();"
Is because I don't want extra white space at the end of the string. As during insert into the database that my code is look like this:
if (IncomeButton.IsChecked == true) {
CategoryType = IncomeButton.Content.ToString();
}else{
CategoryType = ExpensesButton.Content.ToString();
}
During runtime, I found out that why the value look strange in the format of "Expenses " instead of "Expenses"... Therefore I tried with trim of the end part see how & Bingo. I got it working like a charm.
I refered this video to out that how to trace the value: http://www.youtube.com/watch?v=evO3_xutDYI
Thank you all guys for answering my question & sorry for wasting your time & effort to solve my question :) Good luck to all of you & have a nice day.
Need to show a hint, which contains data from a text field. Prompt to appear if the textbox has data.
Just use binding to ToolTipService attached properties. XAML:
<UserControl.Resources>
<converters:IsStringNonemptyConverter x:Key="ToolTipVisibilityConveter" />
</UserControl.Resources>
<TextBox Name="textBox" VerticalAlignment="Center" HorizontalAlignment="Center" Width="150"
ToolTipService.ToolTip="{Binding Text, RelativeSource={RelativeSource Self}}"
ToolTipService.IsEnabled="{Binding Text, RelativeSource={RelativeSource Self}, Converter={StaticResource ToolTipVisibilityConveter}}"/>
Converter:
internal sealed class IsStringNonemptyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return !String.IsNullOrEmpty(value as string);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
You can disable the tooltip using triggers. Place this style in your window or App resources so that it can be shared across all the textboxes in your window or application depending on your choice -
<Style x:Key="{x:Type TextBox}" TargetType="TextBox">
<Style.Triggers>
<Trigger Property="ToolTip" Value="{x:Static sys:String.Empty}">
<Setter Property="ToolTipService.IsEnabled" Value="False" />
</Trigger>
</Style.Triggers>
Make sure you add the system namespace to your xaml -
xmlns:sys="clr-namespace:System;assembly=mscorlib"
I had this problem myself and figured out a different solution. I know this question has been answered but just like me there will still be people coming across this question, and I would like to share my solution:
XAML
<TextBox Name="textBox1" ToolTip="{Binding Text, RelativeSource={RelativeSource Self}}" ToolTipService.IsEnabled="False"/>
Code behind
private void textBox1_TextChanged(object sender, TextChangedEventArgs e)
{
if (textBox1.Text.Length > 0)
{
ToolTipService.SetIsEnabled(textBox1, true);
}
}
I hope this helps someone.
I tried with Visibility Mode & TextChange event. ToolTip invisible when no text. May be useful for someother.
Xaml:
<TextBox Height="23" Width="100" Name="myTextBox" TextChanged="myTextBox_TextChanged" >
<TextBox.ToolTip>
<ToolTip Visibility="Hidden">
<TextBlock Name="toolTipTextBlock"></TextBlock>
</ToolTip>
</TextBox.ToolTip>
</TextBox>
TextChange event handler:
private void myTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
TextBox tb = sender as TextBox;
if (tb.Text.Trim() == "")
{
((ToolTip)tb.ToolTip).Visibility = Visibility.Hidden;
}
else
{
toolTipTextBlock.Text = tb.Text;
((ToolTip)tb.ToolTip).Visibility = Visibility.Visible;
}
}