I'm trying to make a custom converter that inherits from DependencyObject, but it doesn't work:
Converter:
public class BindingConverter : DependencyObject , IValueConverter
{
public object Value
{
get { return (object)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(object), typeof(BindingConverter), new PropertyMetadata(null));
public object Convert(object value, Type targetType, object parameter, Globalization.CultureInfo culture)
{
Debug.Assert(Value != null); //fails
return Value;
}
public object ConvertBack(object value, Type targetType, object parameter, Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Xaml:
<StackPanel x:Name="this">
<!--works-->
<ContentControl Content="{Binding ActualHeight, ElementName=this}"/>
<!--doesn't work-->
<ContentControl>
<Binding>
<Binding.Converter>
<BindingConverter Value="{Binding ActualHeight, ElementName=this}" />
</Binding.Converter>
</Binding>
</ContentControl>
<TextBlock Text="{Binding Animals}"/>
</StackPanel>
Am I missing out anything?
I have some places in my projects where I needed similar functionality. Can't show you exact sample, just an idea:
perhaps you have to inherit from FrameworkElement, not IValueConverter, Something like this:
public class BindingHelper : FrameworkElement
in the BindingHelper class, set Visibility to Collapsed and IsHitTestVisible to false;
to make it working, insert it into visual tree directly. In your example, it should be a child of the StackPanel. So, it will have the same DataContext as other StackPanel children;
then, you can add one ore more dependency properties depending on your needs. For example, you might have single property for the source of data and some different properties which you then will use as converter return values. Handle all changes to the source property in your BindingHelper class and change output properties accordingly;
bind other controls to properties of the BindingHelper class using ElementName syntax
in Silverlight, ActualHeight and ActualWidth properties don't do notifications on property updates. So, binding to them won't work.
Note! ActualHeight property's binding is buggy on binding!
Why you inherit DependencyObject when coding a converter? You should just implement IValueConverter.
Try that,
First add MyConverter by the key of "MyConverterResource" on your resources then,
You can do than on XAML side or on cs side by
//You may do it on XAML side <UserControl.Resources>...
this.Resources.Add("MyConverterResource",new MyConverter());
<TextBlock Text="{Binding ActualHeight,ElementName=this
,Converter=MyConverterResource}"/>
public class MyConverter: IValueConverter
{
public object Convert(object value, Type targetType
, object parameter,Globalization.CultureInfo culture)
{
return "Your Height is:"+Value.toString();
}
}
Hope helps
Related
I have a DependencyProperty in my UserControl with a property changed callback. The property works as expected and the callback works as expected.
public double CurrentFlow
{
get { return (double)GetValue(CurrentFlowProperty); }
set { SetValue(CurrentFlowProperty, value); }
}
public static readonly DependencyProperty CurrentFlowProperty = DependencyProperty.Register("CurrentFlow", typeof(double), typeof(MyUserControl), new PropertyMetadata(0.0, OnCurrentFlowPropertyChanged));
private static void OnCurrentFlowPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
Console.WriteLine("CurrentFlow changed.");
}
However, I have a TextBlock in my UserControl where I want to display CurrentFlow as a formatted string. Currently, I have the Text property of the TextBlock binded to CurrentFlow, and it works, but I'm not getting the format I need. (Too many numbers after the decimal.)
<TextBlock Text="{Binding Path=CurrentFlow, RelativeSource={RelativeSource AncestorType=UserControl}}" />
Ideally, I'd like to have a property named CurrentFlowString that takes the value from CurrentFlow and formats it to what I want. For example: CurrentFlow.ToString("0.00");
What's the best way to go about this with DependencyProperties? I know how to do this with regular properties but I'm kinda stuck here.
Thanks!
If you want to have more flexibility than using StringFormat, you can also use a custom converter. For example,
public class MyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is double d)
return $"{d:f2}";
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
Then add it to your UserControl.Resources, and use it in your Binding:
<UserControl.Resources>
<local:MyConverter x:Key="MyConverter" />
</UserControl.Resources>
<Grid>
<TextBlock Text="{Binding Path=CurrentFlow, RelativeSource={RelativeSource AncestorType=UserControl}, Converter={StaticResource MyConverter}}" />
</Grid>
Solution 2:
Based on your comment below, here's an alternative solution. First, create a new dependency property; for example, FormattedCurrentFlow:
public static readonly DependencyProperty FormattedCurrentFlowProperty = DependencyProperty.Register(
"FormattedCurrentFlow", typeof(string), typeof(MyControl), new PropertyMetadata(default(string)));
public string FormattedCurrentFlow
{
get { return (string)GetValue(FormattedCurrentFlowProperty); }
set { SetValue(FormattedCurrentFlowProperty, value); }
}
Since you already have a method to handle changes in CurrentFlow, update the new FormattedCurrentFlow when CurrentFlow changes:
private static void OnCurrentFlowPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
var myControl = (MyControl)source;
myControl.FormattedCurrentFlow = $"{myControl.CurrentFlow:f2}";
}
The TextBox in the UserControl can now bind to FormattedCurrentFlow:
<TextBlock Text="{Binding Path=FormattedCurrentFlow, RelativeSource={RelativeSource AncestorType=UserControl}}" />
I have a portion of a Window that should display one of several UserControls. Each UserControl presents the same data, only in a different format, arrangement, and style. The particular UserControl that will be presented in this section of the Window should be determined by a single setting that is stored in the ViewModel of the Window.
How can I make it so that the program end user can change the UserControl that is displayed in the Window at run-time?
I figured it out. In my ViewModel, I have a UserControl property called SelectedUC, and another property, called Style, that is an enum type, which enumerates the different UserControls that I am using. In the set part of the Style property I have OnPropertyChanged("SelectedUC"); The get part of the SelectedUC property has a switch-case statement that sets the field of the SelectedUC to a new instance of the corresponding type of UserControl and passes the ViewModel (this) as a parameter.
private MyStyleEnum _style = MyStyleEnum.OneStyle;
public MyStyleEnum Style
{
get { return _style; }
set
{
if (value != _style)
{
_style = value;
OnPropertyChanged("Style");
OnPropertyChanged("SelectedUC");
}
}
}
private UserControl _selectedUC;
public UserControl SelectedUC
{
get
{
switch (Style)
{
case MyStyleEnum.OneStyle:
_selectedUC = new ucOneControl(this);
break;
case MyStyleEnum.AnotherStyle:
_selectedUC = new ucAnotherControl(this);
break;
}
return _selectedUC;
}
set { _selectedUC = value; }
}
In my MainView's xaml, I have a ContentPresenter with the Content property bound to the SelectedUC property in the ViewModel.
<ContentPresenter Content="{Binding SelectedUC}" />
In my SettingsView's xaml, I have a group of RadioButtons that are all bound to the Style property and use a Converter and ConverterParameter.
<Window x:Class="MyProject.View.SettingsView"
xmlns:cv="clr-namespace:MyProject.Converters"
xmlns:vm="clr-namespace:MyProject.ViewModel">
<Window.Resources>
<cv:EnumToBoolConverter x:Key="EBConverter"/>
</Window.Resources>
<RadioButton Content="One" IsChecked="{Binding Path=Style, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource ResourceKey=EBConverter}, ConverterParameter={x:Static Member=vm:MyStyleEnum.SingleLine}}"/>
</Window>
EnumToBoolConverter.cs:
public class EnumToBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (parameter.Equals(value))
return true;
else
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return parameter;
}
}
One (quick but not necessarily best) way is to add a ContentControl to your window
<ContentControl Name="cc" />
Then set the content of it however you like. Eg. set it in code-behind
cc.Content = new UserControl1();
I've written a converter BoolToStringConverter. The converter has two properties TrueString and FalseString. Here's how I've used it in XAML
<UserControl.Resources>
<local:BooleanToStringConverter x:Key="BooleanToStringConverter" TrueString="{Binding Strings.Open, Source={StaticResource MyStrings}}"></local:BooleanToStringConverter>
</UserControl.Resources>
This compiles ok, but I get an xml parse exception when running it. If I change the setting of the TrueString property to TrueString = "Open" it all works fine.
Here's the converter being used:
<Button x:Name="MyButton" Content="{Binding Path=IsOpen, Converter={StaticResource BooleanToStringConverter}}" Command="{Binding MyCommand}" VerticalAlignment="Top" Style="{StaticResource MyStyle}" Margin="0,2,10,2"/>
Any ideas what is wrong? All I want to do as set a property of a local resource to a localized value.
EDIT Here's my converter class
public class BooleanToStringConverter : IValueConverter
{
public BooleanToStringConverter()
{
}
public string TrueString
{
get;
set;
}
public string FalseString
{
get;
set;
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool boolValue = System.Convert.ToBoolean(value, CultureInfo.InvariantCulture);
return boolValue ? TrueString : FalseString;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Here's the runtime exception message:
A first chance exception of type 'System.Windows.Markup.XamlParseException' occurred in System.Windows.dll
Additional information: Set property 'Optimize.Client.Presentation.BooleanToStringConverter.FalseString' threw an exception. [Line: 18 Position: 86]
You cannot bind to the TrueString and FalseString properties. From the MSDN help:
in order to be the target of a binding, the property must be a dependency propert
You can try using the ConverterParameter part of the binding for your xaml
<Button x:Name="MyButton" Content="{Binding Path=IsOpen, Converter={StaticResource BooleanToStringConverter}, ConverterParameter=Open}"
Command="{Binding MyCommand}" VerticalAlignment="Top"
Style="{StaticResource MyStyle}" Margin="0,2,10,2"/>
You could also make your converter less generic and only handle Open/Closed strings.
Another option is to have your value converter extend DependencyObject, and convert your properties to DependencyProperties.
You can also set the public properties in your XAML like this:
<localHelpers:BoolToTextConverter x:Key="boolToTextConverter">
<localHelpers:BoolToTextConverter.TrueText>
Sent
</localHelpers:BoolToTextConverter.TrueText>
<localHelpers:BoolToTextConverter.FalseText>
Not Sent
</localHelpers:BoolToTextConverter.FalseText>
</localHelpers:BoolToTextConverter>
The full example is on my blog post here.
I have a collection
private ObservableCollection<ImageData> imageDataList = new ObservableCollection<ImageData>(); where ImageData is a custom object. It has an attribute called fileName a string that stores full path of an image file. In my XAML code, I have a listbox with datatemplate as the following.
<ListBox Name="listBox_ImageList" Grid.ColumnSpan="3" Grid.Row="2" SelectionChanged="listBox_ImageList_SelectionChanged">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Image Source="{Binding fileName}" Height="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=ListBox, AncestorLevel=1}, Path=ActualHeight}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
After populating ImagaData objects into imageDataList, I set this.listBox_ImageList.ItemsSource = imageDataList;
However, when I run it, I don't see any images. Can you please tell me how to databind properly to a string member of an object to WPF image source?
Checkout this http://social.msdn.microsoft.com/Forums/en-AU/wpf/thread/f94cc770-8d86-4a9f-a5f9-2ee2ea146c1a
Set the DataContext to where the object where the ObservableCollection is located
DateContext = this;
Also instead of fileName bind it to a ImageSource Property or a BitmapImage Property and this is created using the fileName.
To answer your question: You cannot bind the ImageSource property to a string. It works in XAML because WPF uses a default converter from string to ImageSource when you set the value in XAML. If you want to set the value with a binding or from code you need to provide an ImageSource object.
There are 2 ways to do it via binding:
The first one is presented here (the link Juan Carlos mentioned), and it involves creating a IValueConverter that will take your string and transform it to a ImageSource. I would modify the converter code presented there with this:
public sealed class StringToImageSourceConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
try
{
return new BitmapImage(new Uri((string)value));
}
catch
{
return DependencyProperty.UnsetValue;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
The second option is to create your ImageSource in your ImageData class and bind directly to it.
private ImageSource _imageSource
public ImageSource ImageSource
{
get
{
if (_imageSource == null)
{
_imageSource = new BitmapImage(new Uri(fileName), UriKind.RelativeOrAbsolute);
}
return _imageSource;
}
}
hi
i am new to silverlight 4.
i have two radio buttons in my views by name Internal and External in a stackpanel1,which are binded to my view model.
in stackpanel2 i have textblock and button.
now what i need when i select internal radio button stackpanel2 should be visible and on external selection stackpanel2 should be invisible.
so how i bind stackpanel2 visibility property to my viewmodel.
pls solve ma problem as soon as possible.
# anju no-problem, you have to add a new class inherit it with IValueConverter this will be your bool to visibility converter, Now u have to Bind StackPanel Visibility property with boolean property from your view model like this:
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:GreenScreenApp.classes" x:Class="GreenScreenApp.one" >
<UserControl.Resources>
<local:VisibilityConverter x:Key="BoolToVisibilityConverter"/>
</UserControl.Resources>
<Grid DataContext="MyDataSource" x:Name="LayoutRoot" >
<StackPanel x:Name="stackpanel2" Visibility="{Binding IsVisible, Converter={StaticResource BoolToVisibilityConverter}}">
</StackPanel>
</Grid>
you should have a property named "IsVisible" in data context. Converter will convert boolean and return visibility values.
Converter Code
public class VisibilityConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var v = value;
if (v == "true")
return "Visible";
else
return "Collapsed";
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
just want to let you know in my case converter class is in folder "classes" at root of project.
Hope it will solve your problem.
You have to add new Visibility property in your data model and then bind this property to stackpanel Visibility property, further you can bind it with a boolean property too but then you have a need of converter. Converters in silverlight convert any assigned value to a value of property type.
You can learn how to build converter here.
http://weblogs.asp.net/dwahlin/archive/2009/08/15/so-what-s-a-silverlight-value-converter-anyway.aspx