I am receiving the date in this 2018-10-03 format and I would like to switch to this format 03/10/2018
<Label Text="{Binding cmPaymentDate, StringFormat='{0:dd/MM/yyyy}'}" TextColor="White" Font="14"/>
you might try to use converter. Another thing is that it must be a DateTime otherwise stringFormat will have no effect.
<Window x:Class="StackPoC.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:StackPoC"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<StackPanel>
<StackPanel.Resources>
<local:DateConverter x:Key="DateConverter" />
</StackPanel.Resources>
<TextBlock Text="{Binding CmPaymentDate, Converter={StaticResource DateConverter}}" Foreground="Black" FontSize="14"/>
</StackPanel>
Code-Behind
public partial class MainWindow : Window, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private DateTime _cmPaymentDate;
public DateTime CmPaymentDate
{
get
{
return _cmPaymentDate;
}
set
{
_cmPaymentDate = value;
OnPropertyChanged("CmPaymentDate");
}
}
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
CmPaymentDate = new DateTime(2018, 09, 23);
}
// Create the OnPropertyChanged method to raise the event
protected void OnPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
and the converter:
public class DateConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
DateTime dt = (DateTime)value;
return dt.ToString("dd/MM/yyyy");
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Related
I have the following code:
XAML code:
<Window x:Class="combobinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:combobinding"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded">
<Window.Resources>
<local:EnumConverter x:Key="isEnabledConverter" />
</Window.Resources>
<Grid>
<TextBox Text="Hello" IsEnabled="{Binding SectionTitle, Converter={StaticResource isEnabledConverter}}" />
</Grid>
</Window>
C# Code
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public static readonly DependencyProperty SectionTitleProperty =
DependencyProperty.Register(nameof(SectionTitle),
typeof(SectionTitle),
typeof(MainWindow));
public SectionTitle SectionTitle
{
get { return (SectionTitle)GetValue(SectionTitleProperty); }
set { SetValue(SectionTitleProperty, value); }
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
SectionTitle = SectionTitle.TitleBlock;
}
}
public enum SectionTitle
{
Normal,
TitleBlock
}
public class EnumConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var sectionType = (SectionTitle)value;
if (sectionType == SectionTitle.Normal)
return true;
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
I would expect that the EnumConverter would be called as I am setting the DependencyProperty SectionTitle and any breakpoint inside the method will be hit.
However this doesn't seem to be the case; and the IsEnabled property is not being binded to SectionTitle as I wish.
What's wrong with this code?
The problem is the DataContext. The binding does not find its target.
You can set the context in the declaration of the window. Add this to the Window tag in your XAML:
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Define Name property on your Window with Name="MyWindow", then use it in your binding like this:
<TextBox Text="Hello" IsEnabled="{Binding ElementName=MyWindow, Path=SectionTitle, Converter={StaticResource isEnabledConverter}}" />
You need to set the DataContext of your MainWindow. You can esaily do this inside the constructor:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
...
I've bound two radio buttons in my Child window to an Enum in my ViewModel which is constructed in the Main window. The binding works as expected but I have noticed a very odd behavior which I can't solve. I have provided all the code here so you can reconstruct the problem easily for yourself.
Here are the steps to see this odd behavior:
Click on the button in the MainWindow
The ChildWindow opens and the RadioButton is set to User
Choose Automatic and then Choose User again
Close the ChildWindow and reopen it again! Try to change the RadioButton to Automatic. It won't change!
<Window x:Class="RadioButtonBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Button Content="Display Child Window" Click="DisplayChildWindow"/>
</Window>
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
var viewModel = new ViewModel();
DataContext = viewModel;
}
private void DisplayChildWindow(object sender, RoutedEventArgs e)
{
var win = new ChildWindow {DataContext = (ViewModel) DataContext};
win.ShowDialog();
}
}
<Window x:Class="RadioButtonBinding.ChildWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:radioButtonBinding="clr-namespace:RadioButtonBinding"
Title="ChildWindow" Height="300" Width="300">
<Window.Resources>
<radioButtonBinding:EnumBooleanConverter x:Key="EnumBooleanConverter"/>
</Window.Resources>
<StackPanel>
<RadioButton Content="Automatic"
GroupName="CalcMode"
IsChecked="{Binding Path=CalcMode,
Converter={StaticResource EnumBooleanConverter},
ConverterParameter={x:Static radioButtonBinding:CalcMode.Automatic}}"/>
<RadioButton Content="Custom"
GroupName="CalcMode"
IsChecked="{Binding Path=CalcMode,
Converter={StaticResource EnumBooleanConverter},
ConverterParameter={x:Static radioButtonBinding:CalcMode.User}}"/>
</StackPanel>
</Window>
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private CalcMode calcMode = CalcMode.User;
public CalcMode CalcMode
{
get { return calcMode; }
set
{
calcMode = value;
RaisePropertyChanged("CalcMode");
}
}
private void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler == null) return;
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public class EnumBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var paramEnum = parameter as Enum;
var valueEnum = value as Enum;
return Equals(paramEnum, valueEnum);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
var parameterEnum = parameter as Enum;
if (parameterEnum == null)
return DependencyProperty.UnsetValue;
return parameterEnum;
}
}
public enum CalcMode : byte
{
Automatic,
User,
}
UPDATE:
I suspect it must be the Converter but I don't know why? It just falls into a loop.
EDIT
What about converting the enum to bool as follows?
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (parameter == null || !(bool)value)
return DependencyProperty.UnsetValue;
var parameterEnum = parameter as Enum;
return parameterEnum;
}
I am populating my combobox with TimeZone Info using the below in:
MainWindow.xaml.cs
private void Window_Loaded(object sender, RoutedEventArgs e)
{
ReadOnlyCollection<TimeZoneInfo> TimeZones = TimeZoneInfo.GetSystemTimeZones();
this.DataContext = TimeZones;
cmb_TZ.SelectedIndex = 1;
}
The below is from the XAML:
<ComboBox x:Name="cmb_TZ" ItemsSource="{Binding}" Grid.Row="0" Grid.Column="2" Height="28.5" Margin="10,65.375,30.945,0" VerticalAlignment="Top" d:LayoutOverrides="LeftMargin, RightMargin, TopMargin, BottomMargin" SelectionChanged="ComboBox_Selection"/>
I am also able to show the corresponding value in a textbox by using this code:
private void ComboBox_Selection(object Sender, SelectionChangedEventArgs e)
{
var cmbBox = Sender as ComboBox;
DateTime currTime = DateTime.UtcNow;
TimeZoneInfo tst = (TimeZoneInfo)cmbBox.SelectedItem;
txt_Time.Text = TimeZoneInfo.ConvertTime(currTime, TimeZoneInfo.Utc, tst).ToString("HH:mm:ss dd MMM yy");
}
where txt_Time is my text box. The XAML code for it is :
<TextBox x:Name="txt_Time" Grid.Row="0" Grid.Column="1" Height="28.5" Margin="26.148,65.375,28.13,0" TextWrapping="Wrap" VerticalAlignment="Top" d:LayoutOverrides="LeftMargin, RightMargin, TopMargin, BottomMargin"/>
My question is :
Is there a way to accomplish this using Data Binding?
I am able to do this using the straight forward method shown above. But I was wondering if this calculation could be done via Data Binding?
I am new to C#/WPF and I tried creating a simple class and also a class using the INotifyPropertyChanged and referring to it in the MainWindow constructor, but I couldn't get even the combobox populated.
I really want to understand and use the Data Binding magic of C#.
In a standard MVVM approach you would create a view model class with two properties, one read-only collection for all TimeZoneInfos, and one for the currently selected TimeZone.
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ReadOnlyCollection<TimeZoneInfo> TimeZones { get; }
= TimeZoneInfo.GetSystemTimeZones();
private TimeZoneInfo selectedTimeZone = TimeZoneInfo.Local;
public TimeZoneInfo SelectedTimeZone
{
get { return selectedTimeZone; }
set
{
selectedTimeZone = value;
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs("SelectedTimeZone"));
}
}
}
You can set the DataContext of your window to an instance of the view model and bind the ComboBox and TextBox properties like shown in this XAML:
<Window ...>
<Window.DataContext>
<local:ViewModel/>
</Window.DataContext>
<Window.Resources>
<local:TimeZoneConverter x:Key="TimeZoneConverter" />
</Window.Resources>
<StackPanel>
<ComboBox ItemsSource="{Binding TimeZones}"
SelectedItem="{Binding SelectedTimeZone}" />
<TextBox Text="{Binding SelectedTimeZone,
Converter={StaticResource TimeZoneConverter}}"/>
</StackPanel>
</Window>
where the Binding to the Text property uses a Converter like this:
public class TimeZoneConverter : IValueConverter
{
public object Convert(
object value, Type targetType, object parameter, CultureInfo culture)
{
return value == null ? string.Empty : TimeZoneInfo
.ConvertTime(DateTime.UtcNow, TimeZoneInfo.Utc, (TimeZoneInfo)value)
.ToString("HH:mm:ss dd MMM yy");
}
public object ConvertBack(
object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
In your ComboBox, bind its ItemSource to ReadOnlyCollection ,
<ComboBox x:Name="cmb_TZ" ItemsSource="{Binding TimeZoneList}" Grid.Row="0" Grid.Column="2" Height="28.5" Margin="10,65.375,30.945,0" VerticalAlignment="Top" d:LayoutOverrides="LeftMargin, RightMargin, TopMargin, BottomMargin" SelectionChanged="ComboBox_Selection"/>
Now the time you want to display bind to a property,
<TextBox x:Name="txt_Time" Text="{Binding TimeZome}" Grid.Row="0" Grid.Column="1" Height="28.5" Margin="26.148,65.375,28.13,0" TextWrapping="Wrap" VerticalAlignment="Top" d:LayoutOverrides="LeftMargin, RightMargin, TopMargin, BottomMargin"/>
The Corresponding CS file is now,
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
ReadOnlyCollection<TimeZoneInfo> TimeZones = TimeZoneInfo.GetSystemTimeZones();
TimeZoneList = TimeZones;
cmb_TZ.SelectedIndex = 1;
}
private void ComboBox_Selection(object sender, SelectionChangedEventArgs e)
{
var cmbBox = sender as ComboBox;
DateTime currTime = DateTime.UtcNow;
TimeZoneInfo tst = (TimeZoneInfo)cmbBox.SelectedItem;
TimeZome = TimeZoneInfo.ConvertTime(currTime, TimeZoneInfo.Utc, tst).ToString("HH:mm:ss dd MMM yy");
}
private string _TimeZome;
public string TimeZome
{
get { return _TimeZome; }
set
{
if (value == _TimeZome)
return;
_TimeZome = value;
this.OnPropertyChanged("TimeZome");
}
}
private ReadOnlyCollection<TimeZoneInfo> _TimeZoneList;
public ReadOnlyCollection<TimeZoneInfo> TimeZoneList
{
get { return _TimeZoneList; }
set
{
if (value == _TimeZoneList)
return;
_TimeZoneList = value;
this.OnPropertyChanged("TimeZoneList");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Use ObjectDataProvider will simplify your code. Just for this question , actually you don't need to write a View Model in CS.
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:src="clr-namespace:System;assembly=mscorlib"
xmlns:sys="clr-namespace:System;assembly=System.Core"
xmlns:local="clr-namespace:WpfApplication2"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<ObjectDataProvider x:Key="timezone" ObjectType="{x:Type sys:TimeZoneInfo}" MethodName="GetSystemTimeZones"></ObjectDataProvider>
<local:TimeZoneConverter x:Key="timezoneconverter"/>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ComboBox x:Name="cmb_TZ" ItemsSource="{Binding Source={StaticResource timezone}}" Height="30" />
<TextBlock x:Name="txt_Time" Grid.Column="1" Text="{Binding ElementName=cmb_TZ, Path=SelectedValue, Converter={StaticResource timezoneconverter}}"/>
</Grid>
</Window>
And also Clemens' Converter is needed.
public class TimeZoneConverter : IValueConverter
{
public object Convert(
object value, Type targetType, object parameter, CultureInfo culture)
{
return value == null ? string.Empty : TimeZoneInfo
.ConvertTime(DateTime.UtcNow, TimeZoneInfo.Utc, (TimeZoneInfo)value)
.ToString("HH:mm:ss dd MMM yy");
}
public object ConvertBack(
object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
Here's what I would like to end up with:
<Grid Visibility={Binding EnablePurchase, Converter={local:ConditionalConverter TrueValue=(Visibility)Visible FalseValue=(Visibility)Collapsed}}/>
Here's currently what I am doing:
<Grid>
<Grid.Visibility>
<Binding Path="EnablePurchase">
<Binding.Converter>
<local:ConditionalConverter>
<local:ConditionalConverter.TrueValue>
<Visibility>Visible</Visibility>
<local:ConditionalConverter.TrueValue>
<local:ConditionalConverter.FalseValue>
<Visibility>Collapsed</Visibility>
<local:ConditionalConverter.FalseValue>
</local:ConditionalConverter>
</Binding.Converter>
</Binding>
</Grid.Visibility>
</Grid>
You can simply create a converter which has properties like this:
public class ValueConverterWithProperties : IValueConverter
{
public int TrueValue { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if ((int) value == TrueValue)
{
return true;
}
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
and then use it like this:
<Window x:Class="Converter.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:converter="clr-namespace:Converter"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<converter:ValueConverterWithProperties TrueValue="5" x:Key="converterWithProperties"></converter:ValueConverterWithProperties>
</Window.Resources>
<Grid>
<CheckBox IsChecked="{Binding item, Converter={StaticResource converterWithProperties}}"></CheckBox>
</Grid>
you can also derive from MarkupExtension in your converter for a much nicer usage:
public class ValueConverterWithProperties : MarkupExtension, IValueConverter
{
public int TrueValue { get; set; }
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if ((int) value == TrueValue)
{
return true;
}
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
And then you can set the property directly where you are using the converter allowing you to set different values per converter easily:
<Window x:Class="Converter.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:converter="clr-namespace:Converter"
Title="MainWindow" Height="350" Width="525">
<Grid>
<CheckBox IsChecked="{Binding item, Converter={converter:ValueConverterWithProperties TrueValue=5}}"></CheckBox>
<CheckBox IsChecked="{Binding item2, Converter={converter:ValueConverterWithProperties TrueValue=10}}"></CheckBox>
</Grid>
I have searched several tutorials and tried each option, but I cannot get my radio buttons to bind. When I try to compile the following code I get the error
The resource "nullableBooleanConverter" could not be resolved
Here is what I currently have in XAML:
<RadioButton GroupName="grp_Option_1" Content="Yes" IsChecked="{Binding Path=OpstionSelected, Mode=TwoWay, Converter={StaticResource nullableBooleanConverter}, ConverterParameter=true}" />
<RadioButton GroupName="grp_Option_2" Content="No" IsChecked="{Binding Path=OptionSelected, Mode=TwoWay, Converter={StaticResource nullableBooleanConverter}, ConverterParameter=false}" />
My CS has
public bool OptionSelected
{
get { return optionSelected; }
set
{
optionSelected = value;
this.OnPropertyChanged("OptionSelected");
}
}
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(string propName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(
this, new PropertyChangedEventArgs(propName));
}
Here is my converter:
[ValueConversion(typeof(bool?), typeof(bool))]
public class SuccessConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool param = bool.Parse(parameter.ToString());
if (value == null)
{
return false;
}
else
{
return !((bool)value ^ param);
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
bool param = bool.Parse(parameter.ToString());
return !((bool)value ^ param);
}
}
Any help is greatly appreciated!
Try to add the following lines in your <Window.Resources>
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
xmlns:Converters="clr-namespace: here.yournamespace.converts">
<Window.Resources>
<Converters:SuccessConverter x:Key="nullableBooleanConverter" />
</Window.Resources>
<Grid>
</Grid>
</Window>