I need to bind color to fill the rectangle.
XAML:
<Rectangle Fill="{Binding Colorr}"
VerticalAlignment="Center"
Height="3" Width="16"
Margin="3, 1, 5, 0"
Visibility="Visible"/>
ViewModel:
public ItemViewModel()
{
Colorr = Colors.Red;;
}
public Color Colorr
{
get {
return color; }
set
{
color = value;
NotifyOfPropertyChange(() => Colorr);
}
}
The resulting rectangle is not visible (or is transparent - it's hard to say...) instead of being visible and red. How can I get rid of this problem?
The other way around is to use ColorToBrushConverter, just like the one below:
using System.Windows.Data;
using System.Windows.Media;
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 (value as SolidColorBrush).Color;
}
}
Then in XAML define the converter as resource and use it like this:
<Rectangle Fill="{Binding Colorr, Converter={StaticResource ColorToBrushConverter}}"/>
Rectangle.Fill (which it inherits from Shape) is a Brush, not a Color. So make your property a Brush instead:
private Brush _colorr = Brushes.Red;
public Brush Colorr
{
get
{
return _colorr;
}
set
{
_colorr = value;
NotifyOfPropertyChange(() => Colorr);
}
}
There may be other problems, but you need to fix this one first.
I have made minor changes to the Color BrushConverter proposed by Vaidas's to take care of the possible null reference exception:
using System.Windows.Data;
using System.Windows.Media;
namespace Common.Client.Wpf.Converters
{
public class ColorToBrushConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value is null ? null : new SolidColorBrush((Color)value);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return (value as SolidColorBrush)?.Color;
}
}
}
Related
I use a converter to fill the remaining space of a hidden column in WPF (set width to 0.0), but in fact I need the ratio 1.7* vs * if visible!
How can I calculate the real value to set for the converter?
Converter:
public class ColumnWidthConverter : IValueConverter
{
public object Convert(
object value,
Type targetType,
object parameter,
System.Globalization.CultureInfo culture)
{
var isVisible = (bool) value;
var width = parameter as string == "*"
? new GridLength(1, GridUnitType.Star).Value
: double.Parse(parameter as string);
return isVisible ? width : 0.0;
}
public object ConvertBack(
object value,
Type targetType,
object parameter,
System.Globalization.CultureInfo culture)
{
return Binding.DoNothing;
}
}
As you can see I tried with new GridLength(1, GridUnitType.Star).Valuebut it's not the correct way!
XAML (condensed):
<appf:ViewUserControl.Resources>
<local:ColumnWidthConverter x:Key="ColumnWidthConverter" />
</appf:ViewUserControl.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1.7*"/>
<ColumnDefinition Width="{Binding Path=Visible, Converter={StaticResource ColumnWidthConverter}, ConverterParameter=*}"/>
</Grid.ColumnDefinitions>
Visible is just a boolean property in the corresponding ViewModel!
Sometimes it's easy: use string instead of double value
public class ColumnWidthConverter : IValueConverter
{
public object Convert(
object value,
Type targetType,
object parameter,
System.Globalization.CultureInfo culture)
{
var isVisible = (bool) value;
var width = parameter as string;
return isVisible ? width : "0.0";
}
public object ConvertBack(
object value,
Type targetType,
object parameter,
System.Globalization.CultureInfo culture)
{
return Binding.DoNothing;
}
}
I have a combo-box im populating with a collection of objects in my ViewModel.
<ComboBox x:Name="ChangelistComboBox"
SelectedIndex="{Binding MyObjectSelectionIndex, Mode=TwoWay}"
ItemsSource="{Binding MyObjectList, Mode=OneWay}"
Margin="5"
Grid.Column="0">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={StaticResource MyObjectToComboBoxConverter}}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Im using a Converter to convert parameters from the object to a displaystring that shows up in the ComboBox
[ValueConversion(typeof(MyObject), typeof(string))]
class MyObjectToComboBoxConverter : ValueConverterBase
{
public override object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
try {
MyObject theObject = (MyObject)value;
int id = theObject.Id;
return (((id != -1) ? id.ToString() : "default") + " : " + theObject.Description);
} catch(InvalidCastException e) {
return (String)value;
}
}
public override object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
return new MyObject(); //Not used
}
}
abstract class ValueConverterBase : IValueConverter
{
public virtual object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
return DependencyProperty.UnsetValue;
}
public virtual object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
return DependencyProperty.UnsetValue;
}
}
In the Model the list is defined as:
private ObservableCollection<MyObject> _MyObjectList;
public ObservableCollection<MyObject> MyObjectList {
get
{
if (_MyObjectList != null) { return _MyObjectList; } else { return new ObservableCollection<MyObject>(); }
}
set
{
if (_MyObjectList != value) {
_MyObjectList = value;
NotifyPropertyChanged("MyObjectList");
}
}
}
In the ViewModel the MyObjectList is simply referenced from the model through the interface:
public ObservableCollection<MyObject> MyObjectList {
get
{
if (Model != null) {
return Model.MyObjectList;
} else {
return new ObservableCollection<MyObject>();
}
}
}
Without the TryCatch, this converter crashes when my MyObjectList is updated. It gives an error like Cannot cast type string to object on the MyObject theObject = (MyObject)value; line
With the TryCatch the converter works as intended. It even returns a correctly assembled string. The problem is I get InvalidCastExceptions in the error log, which isnt good. Also I have no idea why it works despite the exception.
The only hunch I have is that for some reason the object is being converted twice, once from object to string and then it tries to convert the string to string and fails there. I cant figure out why it would be doing that though.
MVVM IValueConverter Convert method getting empty string argument when expecting a float
Just found this which seems to be the same issue that I'm experiencing. I dont fully understand the explanation for it given there, but if modifying the Convert to accept an empty string is a viable solution i can live with that
Still feels like a hack but beggars cant be choosers i guess
You bind the ComboBox source to a list of MyObject, so the value should never be of type string. I guess there is a problem in your model state.
The way you are binding your view is correct, also the first version of the converter:
[ValueConversion(typeof(MyObject), typeof(string))]
class MyObjectToComboBoxConverter : IValueConverter
{
public override object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
try {
var theObject = (MyObject)value;
var id = theObject.Id;
return (((id != -1) ? id.ToString() : "default") + " : " + theObject.Description);
} catch(InvalidCastException e) {
return (string)value;
}
}
public override object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
throw new NotImplementedException();
}
}
Please start to change your ViewModel to:
public ObservableCollection<MyObject> MyObjectList { get; set; } = new ObservableCollection<MyObject>();
You need to implement the OnPropertyChangeEvent on the properties of the MyObject model (https://learn.microsoft.com/en-us/dotnet/framework/wpf/data/how-to-create-and-bind-to-an-observablecollection)
so you only need to set the ObservableCollection in your ViewModel to an empty instance by default.
Then adding items:
MyObjectList.Add(new MyObject{ id=1, Description="First"});
I'm trying to binding the TextColor from a label on ViewCell:
Label myLabel = new Label { Text = "SomeText" };
myLabel.SetBinding(Label.TextColorProperty,
new Binding("TheTextColor", BindingMode.TwoWay, new LabelTextColorConverter()));
Here's the converter:
public class LabelTextColorConverter : IValueConverter
{
public bool OldValue { get; set; }
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
OldValue = (bool) value;
Debug.WriteLine("asdadasdsadsada");
if ((bool)value)
return Color.Red;
else
return Color.Silver;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
Debug.WriteLine("qwqweqewqeeqe");
return OldValue;
}
}
The debug output doesn't appear, and the color doesn't change either. I don't see anything wrong.
Why do you need Two Way binding for this? I don't think it's necessary.
myLabel.SetBinding(Label.TextColorProperty, new Binding("TheTextColor", BindingMode.OneWay, new LabelTextColorConverter()));
Then:
public class LabelTextColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
bool val = (bool)value;
if (val)
return Color.Red;
else
return Color.Silver;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
... and it should work fine. Also make sure you're setting BindingContext for your page/controls correctly.
This code work well, but the button visibility is collapsed in the design.
how can i set this to visible?
<!--Resources-->
<BooleanToVisibilityConverter x:Key="BoolToVis" />
<Button Visibility="{Binding Converter={StaticResource BoolToVis}, Source={x:Static local:ConfigUser.Prc}}" Grid.Row="1"/>
If I get right what you want. What you need is the button to appear in design mode and also appear when your boolean is set to true at runtime.
You can create your converter that test if it's in design mode in addition of your boolean:
using System;
using System.ComponentModel;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
public class DesignVisibilityConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
if (value is bool) {
return ((bool) value) || DesignerProperties.GetIsInDesignMode(Application.Current.MainWindow)
? Visibility.Visible
: Visibility.Collapsed;
}
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
throw new NotImplementedException();
}
}
This one defaults to visible. Here is my Inverse BoolToVis
[ValueConversion(typeof(bool), typeof(Visibility))]
public class InverseBooleanToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (value != null && (bool)value) ?
Visibility.Collapsed : Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return (value != null) && ((Visibility)value == Visibility.Collapsed);
}
}
I am new to WPF MVVM. Here's what my viewmodel is doing:
A button is pressed and a ping command is launched to see if servers are availables:
-If true, the button is set to Hidden.
-If false, a label with a message ("Servers not availables) is set to
visible
How can I reuse the following IsVisible method to set the Label's visibility?
using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Input;
namespace WpfTest
{
public class PrnViewModel1 : ViewModelBase
{
private ICommand m_PrnServPingCommand;
private Visibility _isVisible=Visibility.Visible;
public PrnViewModel1()
{
PrnServPingCommand = new RelayCommand(new Action<object>(PrnServPing));
}
public ICommand PrnServPingCommand
{
get
{
return m_PrnServPingCommand;
}
set
{
m_PrnServPingCommand = value;
}
}
public void PrnServPing(object obj)
{
string[] serverNames = { "svmsimp1", "svmsimp2" };
bool serversArePingable = Cmethods.PingableAll(serverNames);
if (serversArePingable)
{
IsVisible = Visibility.Hidden; //Button is Hidden
}
else
{
//*** Label with Message "Servers not pingable" set to visible
}
}
public Visibility IsVisible
{
get
{
return _isVisible;
}
set
{
_isVisible = value;
OnPropertyChanged("IsVisible");
}
}
}
}
You can use an IValueConverter to reverse the Visibility on your label.
Here is an example implementation:
public static class Extensions
{
public static System.Windows.Visibility Reversed(this System.Windows.Visibility visibility)
{
if (visibility == System.Windows.Visibility.Visible)
visibility = System.Windows.Visibility.Collapsed;
else if (visibility == System.Windows.Visibility.Collapsed || visibility == System.Windows.Visibility.Hidden)
visibility = System.Windows.Visibility.Visible;
return visibility;
}
}
public class VisibilityReversedConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return ((System.Windows.Visibility)value).Reversed();
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return ((System.Windows.Visibility)value).Reversed();
}
}
Usage:
<TextBlock Text="Servers not available" Visibility="{Binding Visibility, Converter={StaticResource VisibilityReversedConverter}}" ... />
Don't forget to instantiate the converter in the Resources. See here for a tutorial on converters.
I myself often use Visibility type in ViewModel (to avoid writing converters to control layout, because Hidden != Collapsed).
What you need here is either 2 properties to control label and button visibility or single property (bool) to switch which one is displayed and converter (with parameter to invert or just 2 converters).
Here is solution which uses converter and parameter (bonus - MarkupExtension!):
public class SuperConverter : MarkupExtension, IValueConverter
{
public SuperConverter() { }
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool)
{
bool inverted = false;
if (parameter != null && parameter.ToString().Contains("Inverted"))
inverted = true;
return (inverted && (bool)value || !inverted && !((bool)value)) ? Visibility.Hidden : Visibility.Visible;
}
throw new InvalidCastException();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
and use it (you have to have bool IsOperationInProgress in ViewModel, which value will toggle display of either text or button):
<TextBlock Visibility="{Binding IsOperationInProgress, Converter={l:SuperConverter}}" ... />
<Button Visibility="{Binding IsOperationInProgress, Converter={l:SuperConverter}, ConverterParameter=Inverted}" ... />