Change WPF Calendar control day name format - wpf

I am currently using WPF Calendar control. The Day names are displayed as Su, Mo, Tu, etc. But I want those to be displayed as Sun, Mon, Tue, etc.. I found there is no data template property to achieve this one.
Any suggestion will be greatly appreciated.
Thanks

I've looked into this and unfortunately, I don't think that you will be able to achieve what you want.
To start with, I found the StringFormat to show three character day names in the Custom Date and Time Format Strings page at MSDN:
StringFormat=ddd
Then I thought that you might find a solution for the rest of your problem in the Custom date format for WPF Calendar CalendarItems post. So, adapting the idea from #Quartermeister, I could tried the following:
<Calendar>
<Calendar.CalendarButtonStyle>
<Style TargetType="primitives:CalendarButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="primitives:CalendarButton">
<primitives:CalendarButton>
<TextBlock Text="{Binding Day, StringFormat=ddd}"/>
</primitives:CalendarButton>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Calendar.CalendarButtonStyle>
</Calendar>
As you can imagine, I was way off because this answered a different problem. So I went back to MSDN to find the default ControlTemplate for the Calendar control to experiment further. If you look at that ControlTemplate, you will see a Style named CalendarItemStyle.
In that Style is a ControlTemplate and in its Resources section, you will see a DataTemplate with the key {x:Static CalendarItem.DayTitleTemplateResourceKey}. Finally, in that DataTemplate, you will see the TextBlock that is responsible for displaying the day names:
<TextBlock Foreground="#FF333333"
FontWeight="Bold"
FontSize="9.5"
FontFamily="Verdana"
Margin="0,6,0,6"
Text="{Binding}"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
I increased the size of the day names, so we can be sure that that is the correct TextBlock. (Ignore the odd looking Buttons at the top - they are just like that because I didn't copy their ControlTemplates):
Now if you look at the Binding in that TextBlock, you will see that unfortunately, it is set to {Binding}. This means that it is using the whole data bound value, rather than setting a particular StringFormat on a DateTime object. This means that we cannot use a StringFormat because the data bound value is already a string.

Finally, I solved it with following converter,
public class CalendarDayNameConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var daynames = CultureInfo.CurrentCulture.DateTimeFormat.DayNames;
string dayname = value.ToString();
return daynames.First(t => t.StartsWith(dayname)).Substring(0, 3);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}

Late to the party here, but I came up with a slightly different solution that, hopefully, will work a bit more reliably internationally. It's a two part thing. First, make a template for the day name as mentioned by #Sheridan above:
<DataTemplate x:Key="{x:Static CalendarItem.DayTitleTemplateResourceKey}">
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource CalendarDayNameConverter}">
<Binding Path="(Grid.Column)" RelativeSource="{RelativeSource Self}" Mode="OneWay"/>
<Binding Path="FirstDayOfWeek" RelativeSource="{RelativeSource AncestorType={x:Type Calendar}}" Mode="OneWay"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
Of course, format it as per your requirements. The trick here is that the Text is bound to Grid.Column instead of the usual data context value (the two-letter day name provided by WPF). The grid column number is set by internal WPF logic and should be reliable. We have to use an IMultiValueConverter because we depend on the FirstDayOfWeek value from the parent Calendar control too:
public sealed class CalendarDayNameConverter : IMultiValueConverter
{
/// <inheritdoc/>
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values?.Length > 0 && values[0] is int index)
{
var dayNames = culture?.DateTimeFormat?.AbbreviatedDayNames;
if (dayNames?.Length > 0)
{
var firstDayOfWeek = values.Length > 1 && values[1] is DayOfWeek d ? d : culture.DateTimeFormat.FirstDayOfWeek;
return dayNames[(index + (int)firstDayOfWeek) % dayNames.Length];
}
}
return DependencyProperty.UnsetValue;
}
/// <inheritdoc/>
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) =>
null;
}

I've looked into it aswell.
The only solution I have seen was localizing the calendar with a custom localisation dictionary, but unfortunately there are no keys given, we could use.
Decompiling the calendar control I found out, that the calendar control uses a native function (nativeGetCalendarData()) to localize the calendar, so I wont be able to retrieve those keys the easy way :(

Refer this site nullskull.com. The Downloadable Source contain a day name converter and description

Related

Set Multibing for text - get and set

I would like to bind my TextBox.Text to two different sources.
I have 2 ViewModels, one is general ViewModel and one is more specific (which is inherit from its parent).
Both ViewModels has a property called "Hotkey".
I would like to bind my TextBox.Text so it will get the value from the general ViewModel and set it to the specific ViewModel.
I tried the following:
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" TextAlignment="Center" Foreground="#000">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource test}">
<Binding Path="DataContext.Hotkey" RelativeSource="{RelativeSource AncestorType={x:Type MetroStyle:MetroWindow}}" Mode="OneWay" />
<Binding Path="Hotkey" Mode="OneWayToSource"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
It does get the value from the general ViewModel, but it doesn't set its value to the specific one (which inherits from the parent)
I believe the problem may be in the Converter you used for MultiBinding, I've just tried a simple demo and looks like that Converter should be implemented like this:
public class TestConverter : IMultiValueConverter
{
private bool justConvertedBack;
object IMultiValueConverter.Convert(object[] values, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
if (justConvertedBack) {
justConvertedBack = false;
return Binding.DoNothing;
}
return values[0];
}
object[] IMultiValueConverter.ConvertBack(object value, Type[] targetTypes,
object parameter, System.Globalization.CultureInfo culture)
{
justConvertedBack = true;
return new object[] {null, value};
}
}
It happens that after the ConvertBack has been done, the Convert will be triggered and keep the Text of your TextBox unchanged (although you tried deleting/modifying it before). So we need some flag justConvertedBack here to prevent that from occurring.
Currently changing the source from the general ViewModel will change the TextBox's Text but does not update the source from the specific ViewModel. However if setting/typing some value for the TextBox's Text will update the source from the specific ViewModel but won't reflect that value back to the source from the general ViewModel. I hope that behavior is what you want.

Converting controls from MM to Inches in XAML

I have a lot of controls in my XAML in the form of
TextBlock: TextBox.
So for example:
XSize(mm):25.4
YSize(mm):50.8
etc
Now when the user clicks on an option to use imperial units I want to change all textBlocks + textBoxes to something like
XSize(in):1
YSize(in):2
etc
What is the best way to go about this ?
Try to use Converters. Create a MM to Inch converter and use that converter to change the values when the user inputs a value on the certain value.
Sample usage of a converter. You have to define a static resource under the resources of your view for the controls you want to use the converter with
MainWindow.xaml
<Window.Resources>
<spikes:Convertaaa x:Key="Convertaaa" />
</Window.Resources>
<ComboBox x:Name="OptionsToChoose"/>
<TextBox>
<TextBox.Text>
<MultiBinding Converter="{StaticResource Convertaaa}">
<Binding ElementName="OptionsToChoose" Path="SelectedValue"/>
<Binding RelativeSource="{RelativeSource Self}" Path="Text"/>
</MultiBinding>
</TextBox.Text>
</TextBox>
Converter.cs
public class Convertaaa : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
//Implement conversion here. values[0] will give you the selected option values[1] will give you the value to convert, then do a return
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
This assumes that you do know MVVM pattern in WPF and Bindings. If you are doing behind the code, then you probably want to hook up to the TextChanged of your TextBox and instantiate a new Converter Class and call the Convert method and set the Text property of the Textbox.

Binding a cell object's property to a DataGridCell in WPF DataGrid

Using the WPF DataGrid I have the need to change various display and related properties of a DataGridCell - such as Foreground, FontStyle, IsEnabled and so on - based on the relevant value of the cell object property.
Now this is easy to do in code, for example (using an Observable Collection of ObservableDictionaries):
var b = new Binding("IsLocked") { Source = row[column], Converter = new BoolToFontStyleConverter() };
cell.SetBinding(Control.FontStyleProperty, b);
and works fine, however I cannot see how to do this in XAML since I can find no way to set Path to a cell object's property.
One XAML attempts is:
<Setter Property="FontStyle">
<Setter.Value>
<MultiBinding Converter="{StaticResource IsLockedToFontStyleConverter}" Mode="OneWay" UpdateSourceTrigger="PropertyChanged">
<Binding />
<Binding RelativeSource="{x:Static RelativeSource.Self}"/>
</MultiBinding>
</Setter.Value>
</Setter>
but there is no binding to the IsLocked property
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var row = (RowViewModel) values[0];
var cell = (DataGridCell) values[1];
if (cell != null && row != null)
{
var column = DataGridMethods.GetColumn(cell);
return row[column].IsLocked ? "Italic" : "Normal";
}
return DependencyProperty.UnsetValue;
}
Please note that a previous version returned row[col].IsLocked and set the FontStyle using a DataTrigger but a returned object is not databound.
Note, of course, that the application does not know what the columns are at design time.
Finally DataTable's are far too inefficient for my requirements but I would be interested to see how this is done with DataTables anyway, if there is such a solution for them, this might be useful elsewhere (although I prefer using collections).
Surely this is a common issue and I am a WPF noobie trying to go all MVVM on my project, but this issue is holding me back with respect to using the WPF DataGrid.
Well here is the simplest solution I have found. (Actually I had it before I posted this and the other question but was embarrased at such a solution.Since have heard nothing else here and just it is in case anyone else is faced with the same problem, I thought I would share it.)
Put a reference to the cell object in the DataGridCell Tag property. I do this with a combination of XAML and a code binding inside a converter as follows:
<Setter Property="Tag">
<Setter.Value>
<MultiBinding Converter="{StaticResource CellViewModelToTagConverter}" Mode="OneWay" UpdateSourceTrigger="PropertyChanged">
<Binding />
<Binding RelativeSource="{x:Static RelativeSource.Self}"/>
</MultiBinding>
</Setter.Value>
</Setter>
and
public class CellViewModelToTagConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var row = values[0] as RowViewModel;
var cell = values[1] as DataGridCell;
if (row != null && cell != null)
{
var column = DataGridMethods.GetColumn(cell);
// hack within hack!!! (using tag way is itself a hack?)
var b = new Binding("Self") {Source = row[column]};
cell.SetBinding(FrameworkElement.TagProperty, b);
//...
//return row[column];
return DependencyProperty.UnsetValue;
}
return DependencyProperty.UnsetValue;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
You can tell what I think of this solution by my comments inside the converter.(I had to add a Self property to the Cell object and make Self=this in the constructor).
Still it enables my Datagrid coding to be entirely MVVM - if you accept that what I have done inside the converter is consistent with MVVM. Anyway it works!
So doing it this way I can see and manage everything from XAML such as control such binding only on certain columns by placing the XAML within the relevant column cellstyles (that is not doing this via DataGrid.CellStyle).
Anyway, an example of usage is
<Style.Triggers>
<DataTrigger Value="true" Binding="{Binding RelativeSource={RelativeSource Self}, Path=Tag.IsLocked}">
<Setter Property="FontStyle" Value="Italic"/>
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
</Style.Triggers>
On the XAML level it is both simple and IMHO elegant (especially for various ToolTips and Popups for which I make heavy usage of cell object's properties). However I am sure there is a better way of doing this, is there?
Hopefully this all goes away when I can use Net 4.0 and dynamic objects, but for this project I cannot.

Binding a Property of an object in Item (row) to a DataGridCell Property in WPF DataGrid [duplicate]

Using the WPF DataGrid I have the need to change various display and related properties of a DataGridCell - such as Foreground, FontStyle, IsEnabled and so on - based on the relevant value of the cell object property.
Now this is easy to do in code, for example (using an Observable Collection of ObservableDictionaries):
var b = new Binding("IsLocked") { Source = row[column], Converter = new BoolToFontStyleConverter() };
cell.SetBinding(Control.FontStyleProperty, b);
and works fine, however I cannot see how to do this in XAML since I can find no way to set Path to a cell object's property.
One XAML attempts is:
<Setter Property="FontStyle">
<Setter.Value>
<MultiBinding Converter="{StaticResource IsLockedToFontStyleConverter}" Mode="OneWay" UpdateSourceTrigger="PropertyChanged">
<Binding />
<Binding RelativeSource="{x:Static RelativeSource.Self}"/>
</MultiBinding>
</Setter.Value>
</Setter>
but there is no binding to the IsLocked property
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var row = (RowViewModel) values[0];
var cell = (DataGridCell) values[1];
if (cell != null && row != null)
{
var column = DataGridMethods.GetColumn(cell);
return row[column].IsLocked ? "Italic" : "Normal";
}
return DependencyProperty.UnsetValue;
}
Please note that a previous version returned row[col].IsLocked and set the FontStyle using a DataTrigger but a returned object is not databound.
Note, of course, that the application does not know what the columns are at design time.
Finally DataTable's are far too inefficient for my requirements but I would be interested to see how this is done with DataTables anyway, if there is such a solution for them, this might be useful elsewhere (although I prefer using collections).
Surely this is a common issue and I am a WPF noobie trying to go all MVVM on my project, but this issue is holding me back with respect to using the WPF DataGrid.
Well here is the simplest solution I have found. (Actually I had it before I posted this and the other question but was embarrased at such a solution.Since have heard nothing else here and just it is in case anyone else is faced with the same problem, I thought I would share it.)
Put a reference to the cell object in the DataGridCell Tag property. I do this with a combination of XAML and a code binding inside a converter as follows:
<Setter Property="Tag">
<Setter.Value>
<MultiBinding Converter="{StaticResource CellViewModelToTagConverter}" Mode="OneWay" UpdateSourceTrigger="PropertyChanged">
<Binding />
<Binding RelativeSource="{x:Static RelativeSource.Self}"/>
</MultiBinding>
</Setter.Value>
</Setter>
and
public class CellViewModelToTagConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var row = values[0] as RowViewModel;
var cell = values[1] as DataGridCell;
if (row != null && cell != null)
{
var column = DataGridMethods.GetColumn(cell);
// hack within hack!!! (using tag way is itself a hack?)
var b = new Binding("Self") {Source = row[column]};
cell.SetBinding(FrameworkElement.TagProperty, b);
//...
//return row[column];
return DependencyProperty.UnsetValue;
}
return DependencyProperty.UnsetValue;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
You can tell what I think of this solution by my comments inside the converter.(I had to add a Self property to the Cell object and make Self=this in the constructor).
Still it enables my Datagrid coding to be entirely MVVM - if you accept that what I have done inside the converter is consistent with MVVM. Anyway it works!
So doing it this way I can see and manage everything from XAML such as control such binding only on certain columns by placing the XAML within the relevant column cellstyles (that is not doing this via DataGrid.CellStyle).
Anyway, an example of usage is
<Style.Triggers>
<DataTrigger Value="true" Binding="{Binding RelativeSource={RelativeSource Self}, Path=Tag.IsLocked}">
<Setter Property="FontStyle" Value="Italic"/>
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
</Style.Triggers>
On the XAML level it is both simple and IMHO elegant (especially for various ToolTips and Popups for which I make heavy usage of cell object's properties). However I am sure there is a better way of doing this, is there?
Hopefully this all goes away when I can use Net 4.0 and dynamic objects, but for this project I cannot.

WPF : Conditional templating of textblock

I have a bunch of textblocks in an itemscontrol... I need to know how can I underline the text in the textblock based on whether the text is available in a list in the data model..
Sounds very simple to me...but I have been googling since the past 8 hrs...
Can I use datatriggers and valueconverters for this purpose? If yes, then how can I execute the method which lies in the viewModel (the method which helps me to check whther a given a text exists in the data model list)...
Even if I go for conditional templating....how do I access the list which lies in my model (the viewmodel can fetch it...but then how do i access the viewmodel?)..
This should be a fairly easy thing to do...Am I really missing something very simple here?? :)
I am following the MVVM pattern for my application..
One way is to use a multivalueconverter which is a class that implements IMultiValueConverter. A multivalueconverter allows you to bind to several values which means that you can get a reference to both your viewmodel and the text of your TextBlock in your valueconverter.
Assuming that your viewmodel has a method called GetIsUnderlined that returns true or false indicating whether or not the text should be underlined your valueconverter can be implemented along these lines:
class UnderlineValueConverter : IMultiValueConverter
{
#region IMultiValueConverter Members
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var viewmodel = values[0] as Window1ViewModel;
var text = values[1] as string;
return viewmodel.GetIsUnderlined(text) ? TextDecorations.Underline : null;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
You can use this valueconverter in the following way for a TextBlock:
<Grid x:Name="grid1" >
<Grid.Resources>
<local:UnderlineValueConverter x:Key="underlineValueConverter" />
</Grid.Resources>
<TextBlock Text="Blahblah">
<TextBlock.TextDecorations>
<MultiBinding Converter="{StaticResource underlineValueConverter}">
<Binding /> <!-- Pass in the DataContext (the viewmodel) as the first parameter -->
<Binding Path="Text" RelativeSource="{RelativeSource Mode=Self}" /> <!-- Pass in the text of the TextBlock as the second parameter -->
</MultiBinding>
</TextBlock.TextDecorations>
</TextBlock>
</Grid>

Resources