Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
I am trying to bind a foreground color to a WPF Textblock.
The property in the view model is a color, as directed by MvvmCross documentation:
private System.Drawing.Color _setValueColor;
public System.Drawing.Color SetValueColor
{
get { return _setValueColor == null ? System.Drawing.Color.Green : _setValueColor; }
set { SetProperty(ref _setValueColor, value); }
}
The xaml text block is
<TextBlock Grid.Column="3" Grid.Row="1" Margin="20"
Text="{Binding SetMessage}" Foreground="{Binding SetValueColor, Converter={StaticResource NativeColor}}">
</TextBlock>
I have a resources dictionary resource.xaml file which references the plug in. The plug-in is installed as a nu-get MvvmCross.Plugin.Color 7.1.2
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:color="clr-namespace:MvvmCross.Plugin.Color;assembly=MvvmCross.Plugin.Color"
<color:MvxNativeColorValueConverter x:Key="NativeColor" />
</ResourceDictionary>
On the view I add the resource in the constructor
public SimpleReadWriteView()
{
var resourceDict = new System.Windows.ResourceDictionary();
resourceDict.Source = new Uri("Resource.xaml", UriKind.Relative);
Resources.MergedDictionaries.Add(resourceDict);
InitializeComponent();
}
When it's run, it throws a cast error:
InvalidCastException: Unable to cast object of type
'MvvmCross.Plugin.Color.MvxnativeColorValueConverter' to type
'System.Windows.Data.IValueConverter'.
I also tried to create my own converter by inheriting from MvxValueConverter and I get the same type of cast error, the converter cannot be cast to type System.Windows.Data.IValueConverter.
Do not use System.Drawing.Color in a WPF application, it is a WinForms type. Use System.Windows.Media.Color instead:
private System.Windows.Media.Color setValueColor = System.Windows.Media.Colors.Green;
public System.Windows.Media.Color SetValueColor
{
get { return setValueColor; }
set { SetProperty(ref setValueColor, value); }
}
You do not need a Binding Converter. You may instead directly bind the Color property of a SolidColorBrush:
<TextBlock ... >
<TextBlock.Foreground>
<SolidColorBrush Color="{Binding SetValueColor}" />
</TextBlock.Foreground>
</TextBlock>
Besides that, a converter from Color to Brush would look like this:
public class ColorToBrushConverter : IValueConverter
{
public object Convert(
object value, Type targetType, object parameter, CultureInfo culture)
{
return new SolidColorBrush((Color)value);
}
public object ConvertBack(
object value, Type targetType, object parameter, CultureInfo culture)
{
return ((SolidColorBrush)value).Color;
}
}
Related
In WPF, I am trying to have the button created as part of the ItemTemplate in a ItemsControl select the style using a property within the class. I thought I had it coded correctly, but when I run this, it creates the button "Test", but it never even runs the converter for the Style.
By the way, yes, I'm obviously aware that I haven't properly implemented the IValueConverter yet, but when I set breakpoints, it never even enters the converter.
Also, how do I properly keep the style of the button updated when the value of the property CurrentItemProperty changes?
MainWindow.xaml
<Window x:Class="WpfApp1.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:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<local:ButtonStyleConverter x:Key="ButtonStyleConverter"/>
</Window.Resources>
<Grid>
<ItemsControl x:Name="ButtonList">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="OptionButton">
<Button Content="{Binding DisplayName}" Style="{Binding CurrentItem, Converter={StaticResource ButtonStyleConverter}}"></Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Window>
MainWindow.xaml.cs
namespace WpfApp1
{
public partial class MainWindow : Window
{
DependencyProperty CurrentItemProperty = DependencyProperty.Register("CurrentItemProperty", typeof(string), typeof(MainWindow));
public string CurrentItem
{
get
{
return (string)GetValue(CurrentItemProperty);
}
set
{
SetValue(CurrentItemProperty, value);
}
}
public MainWindow()
{
InitializeComponent();
ButtonList.ItemsSource = new OptionButton[]
{
new OptionButton() { DisplayName = "Test"}
};
CurrentItem = "Test";
}
public class OptionButton
{
public string DisplayName { get; set; }
}
}
public class ButtonStyleConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
when I set breakpoints, it never even enters the converter.
Because there's nothing to convert.
If you would run your code in the debugger and look at the debug output, you'd find that you're getting a binding error, where the binding engine fails to find the property CurrentItem on the source object. That's because the source object is a OptionButton instance, which of course does not have a CurrentItem property.
You have a number of options available to you:
Control the style some other way. There's not enough context in your question to know why you're trying to use a converter to return a style, but it's unusual enough an approach that I'll suggest this should be your first thing to try. There's probably a better way to control the styling of the button than using a converter this way.
Use a property that's on the OptionButton type. In your example, the CurrentItem and OptionButton.DisplayName have the same value, so taking the example literally you could just bind to the DisplayName property instead. Alternatively, maybe you could implement a property in OptionButton that delegates to the parent-level value you care about.
Set the binding source to the window, so that the CurrentItem property is visible. There are (at least) two variations on this theme: give the window a name attribute, and refer to it by name in the binding, or just use {RelativeSource AncestorType=Window} markup for the source of the binding.
The bottom line is that the binding doesn't work as expected because the current data context for the binding (which defines the default source for the binding) doesn't have the property you're binding to.
Based on a setting in my application I need to be able to show different formats of showing a Date in XAML. For example it needs to be either in "yyyy/MM/dd" format or in "MM/dd/yyyy" format.
What are some ways that I can achieve this? Please note that the places in my application that I need to handle this are limited so even a "fix on the spot" type of solution would work in my case too.
Thanks for suggestions.
You can use a ValueConverter for this. Just replace YourApplicationSettings.YearFirst with wherever you're pulling the setting from.
public class DateTime2FlexibleDateString : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var dt = value as DateTime?
if (dt== null)
{
return null;
}
var dateTime = dt.Value;
if (YourApplicationSettings.YearFirst)
{
return dateTime.ToString("yyyy/MM/dd");
}
return dateTime.ToString("MM/dd/yyyy");
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
Then either in each xaml file that needs it, or in a App.xaml (or a resource dictionary added to App.xaml), add a static resource for your converter.
<UserControl.Resources>
<converters:DateTime2FlexibleDateString x:Key="DateTime2FlexibleDateString" />
</UserControl.Resources>
And then when you bind to the date, specify the converter.
<TextBlock Text="{Binding MyDateTimeVariable, Converter={StaticResource DateTime2FlexibleDateString}" />
It depends on the context of what you are doing, but if you are binding to a DateTime then you can alter the StringFormat property like so:
<TextBlock Text="{Binding MyDateTimeVariable, StringFormat='yyyy/MM/dd'}" />
<TextBlock Text="{Binding MyDateTimeVariable, StringFormat='MM/dd/yyyy'}" />
Here is a good example of some different format options that be achieved like this.
When I try to bind a valueconverter from a defined enum Status to brush, I get an error in my XAML designer:
'OKStatus' resource not found.
The application works fine runtime, but I'm not able to see my GUI in the designer.
My resources are defined in the color.xaml file, which is read at run time.
All code is within the same namespace
My XAML:
xmlns:config="clr-namespace:App.MyNamespace"
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="c:\Skins\Colors.xaml" />
<ResourceDictionary Source="c:\Skins\Common.xaml" />
</ResourceDictionary.MergedDictionaries>
<config:StatusConverter x:Key="StateConverter" />
<config:BoolConverter x:Key="BoolConverter" />
<config:BooleanConverter x:Key="BooleanConverter" />
</ResourceDictionary>
</UserControl.Resources>
and
Status
My converter:
[ValueConversion(typeof(bool), typeof(Brush))]
public class BoolConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
bool state = (bool)value;
FrameworkElement FrameElem = new FrameworkElement();
if (state == true)
return (FrameElem.FindResource("OKStatus") as Brush);
else
return (FrameElem.FindResource("ErrorStatus") as Brush);
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
return null;
}
}
In this code the frameElem wont have any knowledge of the resources I have defined I guess, so I need a way to get access to my resources during design.
Is this possible?
Yes, it is possible, and your guess is correct. Resource finding starts with the logical tree, and creating a new FrameworkElement() doesn't satisfy this. It's completely disconnected.
What you can do (and what you may have to do if N8's suggestion doesn't work), is to hand your converter a reference to the UserControl as the FrameworkElement to call FindResource() on.
The reason N8's suggestion probably won't work is that Application.Current.FindResource() probably starts at application-level resources and then goes to system resources, but the resources you're after are in the UserControl's resources. If they were placed in App.xaml's resources, it would work. However, I think Application.Current may be null at design-time.
The easiest way I can think of to do this is in your UserControl's constructor:
public MyUserControl(){
var boolconv = new BoolConverter();
boolconv.FrameworkElement = this;
this.Resources.Add( "BoolConverter", boolconv );
InitializeComponent();
}
I'm pretty sure it goes before InitializeComponent(), rather than after.
Doing this in XAML would be more complicated, as you probably have to add a DependencyProperty to your converter so that you could bind the UserControl to it. I think that would be going overboard.
Another way is to put TrueBrush and FalseBrush properties on your converter and assign them in XAML, which is what I tend to do so that my converters are vague and general-use. (NB: Names are slightly different.)
<config:BoolToBrushConverter x:Key="Bool2Brush"
TrueBrush="{StaticResource OKStatusBrush}"
FalseBrush="{StaticResource ErrorStatusBrush}" />
I think the issue is that you are trying to find the resource out of a framework element not in the visual tree. Could you try the following instead?
Application.Current.FindResource("OKStatus") as Brush;
As I have learned by TechNet Wiki, there is necessary to use MultiValue Converter and MultiValueBinding to get correct registred converter and correct FrameworkElement by the UserControl.
XAML Example:
<TextBlock x:Name="tb1" Margin="20">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource MyConverter}">
<Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type UserControl}}"/>
<Binding Path="MyValue"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
Then the converter declaration can looks :
public class MyConverter : IMultiValueConverter
{
FrameworkElement myControl;
object theValue;
public object Convert(object[] values, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
myControl = values[0] as FrameworkElement;
theValue = values[1];
return myControl.FindResource(">>resource u need<<");
}
public object[] ConvertBack(object value, System.Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
.....
}
}
The detail explanation is:
https://social.technet.microsoft.com/wiki/contents/articles/12423.wpfhowto-pass-and-use-a-control-in-it-s-own-valueconverter-for-convertconvertback.aspx
I have run into this problem as well. I think that calling Application.Current is the best way to get at resources from an IValueConverter, since they are not defined on a per window, or page or control, level. This would require the resources to be at least application-level, as stated above.
However, since the Application.Current reference is set to null in the designer, this method will always break the designer. What you appear to have done is given something for the designer to display, while you have given your running application access to resources in the converter.
For all of you out there with this issue, you don't need to implement the Kludge that lewi implemented; this is only if you want the designer surface to load. It does not affect your application while running as the Application.Current call has something to do.
Actually what I ended up doing (for now) was to change from FindResource to TryFindResource, and put the statements in a try/catch block.
This seems to work so far.
try
{
if (state == true)
return (FrameElem.TryFindResource("OKStatus") as Brush);
else
return (FrameElem.TryFindResource("ErrorStatus") as Brush);
}
catch (ResourceReferenceKeyNotFoundException)
{
return new SolidColorBrush(Colors.LightGray);
}
I have binding to Image.Source and TextBlocks. Also I have own convertor classes.
<conv:StatusConvertor x:Key="statusConvertor"></conv:StatusConvertor>
<conv:ImageConvertor x:Key="imageConvertor"></conv:ImageConvertor>
For example:
<Image Source="{Binding Value.profilePhoto, Converter={StaticResource imageConvertor}}" Margin="4,4,4,2"/>
<TextBlock Name="tbStatus" Text="{Binding Value.status,Converter={StaticResource statusConvertor}}" Grid.Column="0" Grid.Row="2" Margin="2,2,2,2" FontSize="11" FontWeight="Normal"></TextBlock>
I want set condition for imageConvertor, for example:
IF tbStatus.Text=="0"
THEN use imageConvertor on Image.Source
It’s possible this write in XAML, maybe in convertor class?
Instead of making your ImageConverter an IvalueConverter, make it an IMultiValueConverter:
<Image Margin="4,4,4,2">
<Image.Source>
<MultiBinding Converter="{StaticResource imageConvertor}">
<Binding Path="Value.profilePhoto" />
<Binding Path="Value.status" />
</MultiBinding>
</Image.Source>
</Image>
A IMultiValueConverter is the same as an IValueConverter, except that it passes an array of objects instead of a single object value.
public object Convert(object[] values,
Type targetType, object parameter, CultureInfo culture)
{
// Use your converter code from before, but add a check for the Status value
// as well
string path = values[0].ToString();
int status = Int32.Parse(values[1].ToString();
if (status == 0)
return newImageSource;
return DependencyProperty.UnsetValue;
}
Here is is tough for me to guess on the design of your current converter, but this gives you a rough idea on what to do. I am implying from your question that if the status is not 0, you don't want your converter to return anything - hence the DependencyProperty.UnsetValue.
I don't think it's possible to do this in XAML.
I'm pretty sure it's impossible to do (as is) in the converter because you don't have access to the sender (here a TextBlock) within.
EDIT : you can do it with a multivalue converter, because you need OneWay Binding. Multivalue converters are difficult to set up with a TwoWay binding (when you need the ConvertBack method).
What I would do would be to declare two Images (one with for each value of your TextBlock : 0 and else) and bind the visibility to the textblock Text value (or directly to Value.status).
This doesn't specifically answer the question, but I managed to solve my problem using this question as a guide, so I thought it might help future searchers. It could probably also be extended further to solve the original question with a little more work.
I was trying to find a way of evaluating an IF-conditional expression within XAML and wanted to combine the power of a Binding expression and a MarkupExtension. Using the idea of a converter, I managed to create a ConditionalMarkupConverter. This has two properties to specify the values to be returned when a binding expression evaluates to true or false, and since it's a converter, it can be easily added to a binding expression.
Converter
public sealed class ConditionalMarkupConverter : MarkupExtension, IValueConverter
{
public object TrueValue { get; set; }
public object FalseValue { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool && (bool)value)
{
// The given value has evaluated to true, so return the true value
return TrueValue;
}
// If we get here, the given value does not evaluate to true, so default to the false value
return FalseValue;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
XAML
<TextBlock Text="{Binding IsActive, Converter={converters:ConditionalMarkupConverter TrueValue=Active, FalseValue=Inactive}}" />
In this example, the TextBlock binds to a boolean property called IsActive and the converter then returns the string "Active" when IsActive is true, or "Inactive" when IsActive is false.
Im attempting to bind to the output of a method. Now I've seen examples of this using ObjectDataProvider However the problem with this is ObjectDataProvider creates a new instance of the object to call the method. Where I need the method called on the current object instance. I'm currently trying to get a converter to work.
Setup:
Class Entity
{
private Dictionary<String, Object> properties;
public object getProperty(string property)
{
//error checking and what not performed here
return this.properties[property];
}
}
My attempt at the XAML
<local:PropertyConverter x:Key="myPropertyConverter"/>
<TextBlock Name="textBox2">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource myPropertyConverter}"
ConverterParameter="Image" >
<Binding Path="RelativeSource.Self" /> <!--this doesnt work-->
</MultiBinding>
</TextBlock.Text>
</TextBlock>
my code behind
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
string param = (string)parameter;
var methodInfo = values[0].GetType().GetMethod("getProperty", new Type[0]);
if (methodInfo == null)
return null;
return methodInfo.Invoke(values[0], new string[] { param });
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException("PropertyConverter can only be used for one way conversion.");
}
My problem is that I cant seem to pass the current Entity into the converter. So When i try to use reflection to get the getProperty method I have nothing to operate on
thanks, steph
Wrap the call to the method inside a get property and add this get property to whatever class that is your current DataContext.
Edit: Answering your updated question.
If you only pass one parameter to the valueconverter you don't need a multivalueconverter, just use a regular valueconverter (implementing IValueConverter). Also, why not cast the object in the valueconverter to a Distionary and use it directly instead of using reflection.
To pass current datacontext as a binding do this: <Binding . />. I'm guessing the datacontext of the textblock is entity.
Still, all this is not necessary if all you want to do is run some code before accessing a dictionary item. Just use an index property instead, you can databind to it directly:
public class Entity
{
private Dictionary<String, Object> properties;
public object this[string property]
{
get
{
//error checking and what not performed here
return properties[property];
}
}
}
<TextBlock Text="{Binding Path=[Image]}" />