I have a xamDataGrid where I read in data from a file. This field is of type datetime. I would like to change the foreground color of this column when the date loaded is greater than todays date. I have a following IValueConverter:
public class MyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value != null)
{
var color = new SolidColorBrush(Colors.Red);
int result = DateTime.Compare((DateTime)value, todaysDate);
if (result > 0)
return color = new SolidColorBrush(Colors.Red);
else
return color = new SolidColorBrush(Colors.White);
}
return Binding.DoNothing;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
I get and error from this: (DateTime)value
When I step through the code it seems that the if (value !=null) is executed even though there is no data being displayed in the datagrid. Not sure why this is in this path. It should only be executed when I'm loading a file into the datagrid.
Here is the xaml:
<viewModel:MyConverter x:Key="myDateConv"/>
<igDP:UnboundField Name="My Date" BindingPath="MyDate" Width="Auto" BindingMode="TwoWay" >
<igDP:UnboundField.Settings>
<igDP:FieldSettings AllowEdit="True" >
<igDP:FieldSettings.EditorStyle>
<Style TargetType="{x:Type igEditors:XamDateTimeEditor}" >
<Setter Property="Mask" Value="mm/dd/yyyy" />
<Setter Property="Foreground" Value="{Binding RelativeSource={x:Static RelativeSource.Self},Converter={StaticResource myDateConv}}"/>
</Style>
</igDP:FieldSettings.EditorStyle>
</igDP:FieldSettings>
</igDP:UnboundField.Settings>
</igDP:UnboundField>
here is the class:
public class myPrice : INotifyPropertyChanged
{
private DateTime myDate;
public DateTime MyDate
{
get { return myDate; }
set
{
if (myDate != value)
{
priceDate = value;
NotifyPropertyChanged("MyDate");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
Related
I want when visibility of "StckPnl1" is set to Collapsed, my property "IsBusyIndicatorShowing" of "BusyDeco1" is being set to true.
What's the simplest solution?
<ctrls:BusyDecorator x:Name="BusyDeco1" IsBusyIndicatorShowing="??" Style="{DynamicResource BusyDecorator1}"/>
<StackPanel x:Name="StckPnl1" Visibility="Collapsed">
Use a DataTrigger:
<ctrls:BusyDecorator>
<ctrls:BusyDecorator.Style>
<Style TargetType="{x:Type ctrls:BusyDecorator}" BasedOn="{DynamicResource BusyDecorator1}">
<Setter Property="IsBusyIndicatorShowing" Value="False" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=StckPnl1, Path=Visibility}" Value="Collapsed">
<Setter Property="IsBusyIndicatorShowing" Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
</ctrls:BusyDecorator.Style>
</ctrls:BusyDecorator>
i use a converter for this stuff.
<ctrls:BusyDecorator x:Name="BusyDeco1"
IsBusyIndicatorShowing="{Binding ElementName=StckPnl1, Path=Visibility, Converter={StaticResource TrueIfNotVisibleConverter}}" Style="{DynamicResource BusyDecorator1}"/>
<StackPanel x:Name="StckPnl1" Visibility="Collapsed">
<Converter:BoolToVisibilityConverter x:Key="TrueIfVisibleConverter" Inverted="False" Not="False" />
<Converter:BoolToVisibilityConverter x:Key="TrueIfNotVisibleConverter" Inverted="False" Not="True" />
<Converter:BoolToVisibilityConverter x:Key="VisibleIfTrueConverter" Inverted="True" Not="False" />
<Converter:BoolToVisibilityConverter x:Key="VisibleIfNotTrueConverter" Inverted="True" Not="True" />
public class BoolToVisibilityConverter : IValueConverter
{
private bool inverted = false;
private bool not = false;
public bool Inverted
{
get { return inverted; }
set { inverted = value; }
}
public bool Not
{
get { return not; }
set { not = value; }
}
private object VisibilityToBool(object value)
{
if (!(value is Visibility))
return DependencyProperty.UnsetValue;
return (((Visibility)value) == Visibility.Visible) ^ Not;
}
private object BoolToVisibility(object value, object parameter)
{
if (!(value is bool))
return DependencyProperty.UnsetValue;
var hiddenodercollapsed = Visibility.Collapsed;
if (parameter != null && parameter.ToString().ToLower().Contains("hidden"))
hiddenodercollapsed = Visibility.Hidden;
return ((bool)value ^ Not) ? Visibility.Visible : hiddenodercollapsed;
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return Inverted ? BoolToVisibility(value, parameter) : VisibilityToBool(value);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Inverted ? VisibilityToBool(value) : BoolToVisibility(value, parameter);
}
}
I am new to WPF and am a little lost.
I want to display Text within a label binding it to the following class:
class Status
{
public string Message;
public bool Success;
}
I want the label to display the "message" in green if success and in red if not. I am not sure how to start on it.
First, you need to bind to properties, not members. You should also get into the habit of implementing INotifyPropertyChanged on your class that you're binding to.
public class Status : INotifyPropertyChanged
{
private string message;
public string Message
{
get { return this.message; }
set
{
if (this.message == value)
return;
this.message = value;
this.OnPropertyChanged("Message");
}
}
private bool success;
public bool Success
{
get { return this.success; }
set
{
if (this.success == value)
return;
this.success = value;
this.OnPropertyChanged("Success");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
In terms of binding, you'd have to use a custom IValueConverter
public class RGColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null)
return null;
bool success = (bool) value;
return success ? Brushes.Green : Brushes.Red;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
And the relevant binding/setup
<Window.Resources>
<wpfApplication2:RGColorConverter x:Key="colorConverter" />
</Window.Resources>
<Label Content="{Binding Message}" Foreground="{Binding Success, Converter={StaticResource colorConverter}}" />
I would like to create WPF converter that converts double number to "+" or "-" String based on the number Sign(positive or negative)
but I can't handle the converted back method because I don't have the number anymore.
The "+" and "-" signs bounded to a combobox at the xaml side
any ideas???
public class AmountToDebitCreditConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if ((value == null) || (System.Convert.ToDecimal(value) == 0))
return string.Empty;
return System.Convert.ToDecimal(value) > 0 ? "+" : "-";
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
KeyValue kv = value as KeyValue;
if ((String)value == "+")
return 1;
else
return -1;
}
}
Here's my Xaml code
<igDP:UnboundField Name="ActualAdjustmentAmount" Label="PlusMinusKey" Converter={StaticResource signConverter} >
<igDP:Field.Settings>
<igDP:FieldSettings EditorType="{x:Type igEditors:XamComboEditor}" LabelWidth="40" CellWidth="40">
<igDP:FieldSettings.EditorStyle>
<Style TargetType="{x:Type igEditors:XamComboEditor}">
<Setter Property="ItemsSource" Value="{Binding Path=Flags, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainWindow}}}" />
<Setter Property="FlowDirection" Value="LeftToRight"/>
<Setter Property="SelectedItem" Value="{Binding Path=ActualAdjustmentAmount }" />
</Style>
</igDP:FieldSettings.EditorStyle>
</igDP:FieldSettings>
</igDP:Field.Settings>
</igDP:UnboundField>
If the idea is not just to be able to use (and exploit) Converters, then I would rather suggest you to have a specific Sign property in your ViewModel and bind your view showing sign with that property.
It is probably best to do this sort of conversion in a view-model whenever possible.
Nonetheless, you've pinpointed the problem -- you no longer have the number after your conversion. The fact is that you don't need to return a string from your converter -- you can return any object that will return the desired string from its ToString() override, and that object can contain any other data and behaviour that you want.
Here is a converter that should accomplish what you're after, though I haven't actually tested this:
public class SignedDoubleConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return new DisplaySignedDouble(value);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
var d = value as DisplaySignedDouble;
return (d == null || !d.Value.HasValue)
? 0d
: d.Value.Value;
}
private class DisplaySignedDouble
{
public DisplaySignedDouble(object value)
{
Value = value is double ? (double) value : (double?) null;
}
public double? Value { get; private set; }
public override string ToString()
{
if (!Value.HasValue || Value.Value == 0d)
{
return string.Empty;
}
return Value.Value > 0 ? "+" : "-";
}
}
}
Background.
I am developing a stock trading application.
Which obviously have a market watch.
I am developing this market watch using Datagrid.
What Does the Grid do?
It displays price points of a stock.
Every time a stock value increases the particular cell foreground turns green
if it decreases it turns red.
What i did?
I tried to use the value converter method and multibinding
Problem.
The value converter gives the current value only.
How can i pass the old value to that converter.
Code:
<wpfTlKit:DataGrid.CellStyle>
<Style TargetType="{x:Type wpfTlKit:DataGridCell}">
<Setter Property="Background">
<Setter.Value>
<MultiBinding Converter="{StaticResource myHighlighterConverter}"
>
<MultiBinding.Bindings>
<Binding RelativeSource="{RelativeSource Self}"></Binding>
<Binding Path="Row" Mode="OneWay"></Binding>
<Binding ElementName="OldData" Path="Rows"></Binding>
</MultiBinding.Bindings>
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</wpfTlKit:DataGrid.CellStyle>
Converter
public class HighlighterConverter : IMultiValueConverter
{
#region Implementation of IMultiValueConverter
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values[1] is DataRow)
{
//Change the background of any cell with 1.0 to light red.
var cell = (DataGridCell)values[0];
var row = (DataRow)values[1];
var columnName = cell.Column.SortMemberPath;
if (row[columnName].IsNumeric() && row[columnName].ToDouble() == 1.0)
return new SolidColorBrush(Colors.LightSalmon);
}
return SystemColors.AppWorkspaceColor;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new System.NotImplementedException();
}
#endregion
}
public static class Extensions
{
public static bool IsNumeric(this object val)
{
double test;
return double.TryParse(val.ToString(), out test);
}
public static double ToDouble(this object val)
{
return Convert.ToDouble(val);
}
}
To change the color in a DataGrid cell I recommend the following:
Build a Model that implements INotifyPropertyChanged that contains the current, and previous price plus a property that reflects the change in the price (I've attached the full model at the end of this answer).
public double ChangeInPrice
{
get
{
return CurrentPrice - PreviousPrice;
}
}
And set the Background of the CellTemplate in your DataGrid based on the change in price using a Converter.
Note: INotifyPropertyChanged helps to change the color of the cell when the price values change.
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock
Text="{Binding Path=CurrentPrice}"
Background="{Binding Path=ChangeInPrice, Converter={StaticResource backgroundConverter}}" >
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
[ValueConversion(typeof(object), typeof(SolidBrush))]
public class ObjectToBackgroundConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
SolidColorBrush b = Brushes.White;
try
{
double stock = (double)value;
if (stock > 0)
{
b = Brushes.Green;
}
else if (stock < 0)
{
b = Brushes.Red;
}
}
catch (Exception e)
{
}
return b;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Here is full model for completeness:
public class Stock : INotifyPropertyChanged
{
public Stock(string stockName, double currentPrice, double previousPrice)
{
this.StockName = stockName;
this.CurrentPrice = currentPrice;
this.PreviousPrice = previousPrice;
}
private string _stockName;
public String StockName
{
get { return _stockName; }
set
{
_stockName = value;
OnPropertyChanged("StockName");
}
}
private double _currentPrice = 0.00;
public double CurrentPrice
{
get { return _currentPrice; }
set
{
_currentPrice = value;
OnPropertyChanged("CurrentPrice");
OnPropertyChanged("ChangeInPrice");
}
}
private double _previousPrice = 0.00;
public double PreviousPrice
{
get { return _previousPrice; }
set
{
_previousPrice = value;
OnPropertyChanged("PreviousPrice");
OnPropertyChanged("ChangeInPrice");
}
}
public double ChangeInPrice
{
get
{
return CurrentPrice - PreviousPrice;
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
well i think the problem is not the datagrid, but the object you bind to. if you bind to a datatable the oldvalues are built in (DataRowVersion). if you have other entity objects, then this entities need to support the original and modified values.
I have:
public class Person
{
String name { get; set; }
String address { get; set; }
bool isMarried { get; set; }
}
My datagrid gets populated with a list of persons.
I want to have a custom column where icon-1.jpg is displayed when isMarried is true and icon-2.jpg is displayed when isMarried is false.
How do I do this in WPF ? Any ideas ?
I know how to do a custom column but I do not know how to assoc the two states of isMarried with icon-1.jpg and icon-2.jpg.
You could do this with a DataTrigger in your custom column:
<DataGridTemplateColumn Header="Married">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Image x:Name="IMG" Source="married_image" />
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=isMarried}" Value="False">
<Setter Property="Source" Value="not_married_image" TargetName="IMG"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
You can use an IValueConveter to convert from a Boolean value to an Uri (Uri is what you need for image source).
public class MarriedConverter : IValueConverter
{
public Object Convert(Object value, Type targetType, Object parameter, CultureInfo culture)
{
if ((value == null) || !(value is bool))
return null;
bool isMarried = (bool)value;
if (isMarried)
return new Uri(#1);
else
return new Uri(#2);
}
public Object ConvertBack(Object value, Type targetType, Object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}