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:
Related
in the following structure
<Border ...>
<ItemsControl>
<ItemsControl.Template>
<DataTemplate>
<ACustomElement>
<Border MouseLeftButtonDown="method1">
</ACustomElement>
</DataTemplate>
</ItemsControl.Template>
</ItemsControl>
</Border>
I want to call a public method in the ACustomElement class from inside method1().
What I tried so far in method1():
var cr = ((Border)sender).Parent;
cr.method2();
method2 is a public method in my ACustomElement class. But it doesn't seem to recognize the method.
I'm getting the following error:
'DependencyObject' does not contain a definition for 'method2' and no extension method 'method2' accepting a first argument of type 'DependencyObject' could be found (are you missing a using directive or an assembly reference?)
Any suggestions on how to solve this problem?
Certainly I'm just missing a cast or something else...
Edit: The following style will always be applied to ACustomElement:
<Style TargetType="{x:Type c:ACustomElement}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type c:ACustomElement}">
<ContentPresenter Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
You have to cast cr to ACustomElement type
var cr = (ACustomElement)((Border)sender).Parent;
cr.method2();
otherwise, your cr variable refers to DependencyObject type as you see in the exception.
if you are not sure about hierarchy use this method to find a parent of specific type.
private T FindParent<T>(DependencyObject child) where T : DependencyObject {
var parent = VisualTreeHelper.GetParent(child) as T;
if (parent != null)
return parent;
return FindParent<T>(parent);
}
// usage
private void method1(object sender, MouseButtonEventArgs e)
{
var cr = FindParent<ACustomElement>((Border)sender);
}
Also, DateTemplate can be child of ItemsControl.ItemTemplate, but not of ItemsControl.Template (which expects ControlTemplate)
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:ACustomElement>
<Border MouseLeftButtonDown="method1" />
</local:ACustomElement>
</DataTemplate>
</ItemsControl.ItemTemplate>
Update
And as I pointed in the comment to the question, add an event handler to your type directly or bind a command. Why do you make it so complicated?
Try to bind the Tag property of the Border to the custom control:
<ACustomElement>
<Border MouseLeftButtonDown="method1" Tag="{Binding RelativeSource={RelativeSource AncestorType=ACustomElement}}">
</ACustomElement>
...and cast the Tag property in the event handler:
var cr = sender as Border;
var ctrl = cr.Tag as ACustomElement;
ctrl.method2();
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.
I know that my question was stated and answered in several ways here already. But I just can't get it to run the way I would like.
As the title states, I try to change the background color of my datagridcell depending on its content.
I am relatively new to WPF, but I guess the solution is a converter combined with a binding.
The goal is to change the background colour of the cell in dependence of a property called "Status", which is an enum with four states.
I already wrote a converter:
using System;
using System.Windows.Data;
using System.Drawing;
using System.Windows;
namespace Admin
{
[ValueConversion(typeof(Member.UserStatus), typeof(Brushes))]
public class StatusToColorConverter : IValueConverter
{
#region IValueConverter Member
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var enumValue = (Member.UserStatus)value;
if(enumValue == Member.UserStatus.Change)
return Brushes.Red;
if(enumValue == Member.UserStatus.Import)
return Brushes.Blue;
if(enumValue == Member.UserStatus.Remove)
return Brushes.Orange;
if(enumValue == Member.UserStatus.Synced)
return Brushes.Green;
else
return DependencyProperty.UnsetValue;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
}
So far so good.
The xaml part looks like this (after some googling...)
<DataGrid.Columns>
<DataGridTemplateColumn Header="Status">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Status}" Background="{Binding Status, Converter={StaticResource StatusToColorConverter}}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
I don't really get (yet) what DataGridTemplateColums and DataTemplates are. But I tried the same with CellStyle and another way that I can't remember.
The converter has a breakpoint and gets called in all solutions I tried. But somehow the "Background" property seems to ignore the return value from the converter. So I guess my fault (or my lack of understanding) has another source.
Anyone care to point me in the right direction?
If I interpret your XAML correctly - you are painting the column HEADER (the "topmost" cell), not the cell itself.
This works for me:
<DataGrid.CellStyle>
<Style TargetType="{x:Type DataGridCell}">
<Setter Property="Background" Value="{Binding Status},
Converter={StaticResource StatusToColorConverter}">
</Setter>
</Style>
</DataGrid.CellStyle>
This should paint your cell
As a side note: Your cells might look "weird" if you are painting their Background - my cells often lose their Borders etc. A quick and easy fix is to add the following Propertie Setters (it is the default wpf style)
<Setter Property="BorderThickness" Value="1,0,1,1"></Setter>
<Setter Property="BorderBrush" Value="#FF000000"></Setter>
I did it this way:
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="Background">
<Setter.Value>
<Binding Path="Row" Mode="OneWay" />
</Setter.Value>
</Setter>
</Style>
</DataGrid.CellStyle>
Note that I am using DataView as ItemsSource to DataGrid and therefore I bind to "Row". Item is than basically DataRowView. Then you need to access row values in the converter:
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
var enumValue = (Member.UserStatus)((DataRow)value)["Status"];
if(enumValue == Member.UserStatus.Change)
return Brushes.Red;
if(enumValue == Member.UserStatus.Import)
return Brushes.Blue;
if(enumValue == Member.UserStatus.Remove)
return Brushes.Orange;
if(enumValue == Member.UserStatus.Synced)
return Brushes.Green;
else
return DependencyProperty.UnsetValue;
}
Ok guys. I have the problem solved now.
The problem wasn't my xaml code. The converter worked also fine. The Problem was the following line in the converter .cs file:
using System.Drawing;
I replaced it with the correct one:
using System.Windows.Media;
And now it works like a charm.
...
I was passing Brushes for WinForms instead of WPF Brushes!
Thanks for all the help anyway :)
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.
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;
}
}