I would like to call the converter's Convert method though there is no value change in the source. Now it is calling first time only. Is there any way to achieve this?
I know that convert method will not call every time unless there is a change is the source.
public class ElementNameToDrawingBrushConverter : IValueConverter
{
/// <summary>
/// Method converts colors to brush.
/// </summary>
/// <param name="value">value to convert</param>
/// <param name="targetType">target type</param>
/// <param name="parameter">param for convert</param>
/// <param name="culture">culture info instance</param>
/// <returns>converted brush</returns>
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
DrawingBrush settedBrush = null;
var shape = value as Shape;
if (shape == null || parameter == null)
{
return value;
}
settedBrush = shape.Fill as DrawingBrush;
return GetIconBrush(settedBrush, parameter as Brush);
}
/// <summary>
/// This method is to provide the new brush for the image
/// </summary>
/// <param name="settedBrush">settedBrush</param>
/// <param name="brushColor">brushColor</param>
/// <returns>DrawingBrush</returns>
public static DrawingBrush GetIconBrush(DrawingBrush settedBrush, Brush brushColor)
{
DrawingBrush newBrush = null;
if (settedBrush != null && brushColor != null)
{
newBrush = settedBrush.Clone();
foreach (var geometry in ((DrawingGroup)newBrush.Drawing).Children)
{
GeometryDrawing geometryDrawing = geometry as GeometryDrawing;
if (geometryDrawing != null)
{
if (geometryDrawing.Pen != null)
{
if (geometryDrawing.Pen.Brush != null)
{
geometryDrawing.Pen.Brush = brushColor;
}
}
else if (geometryDrawing.Brush != null)
{
geometryDrawing.Brush = brushColor;
}
}
DrawingGroup drawingGroup = geometry as DrawingGroup;
if (drawingGroup != null)
{
foreach (var geometryInner in drawingGroup.Children)
{
GeometryDrawing geometryDrawingChild = geometryInner as GeometryDrawing;
if (geometryDrawingChild != null)
{
if (geometryDrawingChild.Pen != null)
{
geometryDrawingChild.Pen.Brush = brushColor;
}
else if (geometryDrawingChild.Brush != null)
{
geometryDrawingChild.Brush = brushColor;
}
}
}
}
}
return newBrush;
}
return settedBrush;
}
/// <summary>
/// Convert back implementation
/// </summary>
/// <param name="value">value to convert back</param>
/// <param name="targetType">target type</param>
/// <param name="parameter">param for convert</param>
/// <param name="culture">culture info instance</param>
/// <returns>fallback object</returns>
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
<Button Template="{StaticResource CustomButton}" Content="{Binding MyContent}" ImageContent="{StaticResource SystemResetNormal}"/>
<ControlTemplate TargetType="{x:Type Button}" x:Key="CustomButton">
<Border Background="Pink" Height="80" Width="70">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="60"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Rectangle x:Name="buttonRectangle" Fill="{TemplateBinding ImageContent}" Height="60" Width="40" Loaded="Rectangle_Loaded" />
<TextBlock x:Name="ContentTxt" Text="{TemplateBinding Content}" Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="buttonRectangle" Property="Fill" Value="{Binding ElementName=buttonRectangle,
Converter={StaticResource ElementNameToBrushConverter},
ConverterParameter={StaticResource BAquamarine}}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
I need to call Convert method every time when i mouse over it.
Try to bind to ImageContent instead of buttonRectangle:
<Setter TargetName="buttonRectangle" Property="Fill" Value="{Binding ImageContent,
Converter={StaticResource ElementNameToBrushConverter},
ConverterParameter={StaticResource BAquamarine}}" />
Then the converter should be invoked whenever you set the ImageContent property provided that you implement the INotifyPropertyChanged interface correctly.
In your converter, you should then cast the actual value to a Brush instead of a Shape:
var settleBrush = value as DrawingBrush;
...
Related
i have custom usercontrol, which must change some boolean variable in window and get value from some binding. But when i try get at first time value it equal to default value - null. And when i do action it`s work good.
<catel:UserControl x:Class="App.Shell.Controls.ToggleButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:catel="http://catel.codeplex.com"
xmlns:debug="clr-namespace:System.Diagnostics;assembly=System">
<!-- Content -->
<Grid>
<Grid.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Width" Value="37" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Label
Margin="0"
Padding="0"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
Background="#010000"
Foreground="#FEFEFF"
FontSize="12">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Label>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding IsTurnedOn}" Value="True">
<Setter Property="Content" Value="Turn off" />
</DataTrigger>
<DataTrigger Binding="{Binding IsTurnedOn}" Value="False">
<Setter Property="Content" Value="Turn on" />
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Resources>
<Button>
<Button.InputBindings>
<MouseBinding Command="{Binding ToggleCmd}" MouseAction="LeftDoubleClick" />
</Button.InputBindings>
</Button>
</Grid>
</catel:UserControl>
with catel viewmodel
namespace App.Shell.Controls
{
using Catel.Data;
using Catel.MVVM;
using System;
using System.Windows.Input;
/// <summary>
/// UserControl view model.
/// </summary>
public class ToggleButtonViewModel : ViewModelBase
{
/// <summary>
/// Initializes a new instance of the <see cref="ToggleButtonViewModel"/> class.
/// </summary>
public ToggleButtonViewModel()
{
ToggleCmd = new Command(OnToggleCmdExecute);
}
/// <summary>
/// Gets the title of the view model.
/// </summary>
/// <value>The title.</value>
public override string Title { get { return "ToggleButton"; } }
public Command ToggleCmd { get; private set; }
private void OnToggleCmdExecute()
{
IsTurnedOn = !IsTurnedOn;
}
public bool IsTurnedOn
{
get { return GetValue<bool>(IsTurnedOnProperty); }
set { SetValue(IsTurnedOnProperty, value); }
}
public static readonly PropertyData IsTurnedOnProperty = RegisterProperty("IsTurnedOn", typeof(bool), setParent: false, createDefaultValue: null);
// TODO: Register models with the vmpropmodel codesnippet
// TODO: Register view model properties with the vmprop or vmpropviewmodeltomodel codesnippets
// TODO: Register commands with the vmcommand or vmcommandwithcanexecute codesnippets
}
}
and code behind
namespace App.Shell.Controls
{
using Catel.Windows.Controls;
using System.Windows;
using System.Windows.Input;
using Catel.MVVM.Views;
using System;
using Catel.MVVM;
/// <summary>
/// Interaction logic for ToggleButton.xaml.
/// </summary>
public partial class ToggleButton : UserControl
{
/// <summary>
/// Initializes a new instance of the <see cref="ToggleButton"/> class.
/// </summary>
public ToggleButton()
{
InitializeComponent();
}
[ViewToViewModel(MappingType = ViewToViewModelMappingType.TwoWayViewWins)]
public bool IsTurnedOn
{
get { return (bool)GetValue(IsTurnedOnProperty); }
set { SetValue(IsTurnedOnProperty, value); }
}
public static readonly DependencyProperty IsTurnedOnProperty =
DependencyProperty.Register("IsTurnedOn", typeof(bool), typeof(ToggleButton), new PropertyMetadata(null));
}
}
i insert that control to my catel window
<ctrls:ToggleButton IsTurnedOn="{Binding Path=IndividualSpeechTimerState, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}" />
When i try get property IsTurnedOn from UserControl at first time it equal to default value.
Since you defined [ViewToViewModel(MappingType = ViewToViewModelMappingType.TwoWayViewWins)], it'll always write the first time property of the view. This allows you to externally bind this value.
Make sure to set a valid value in your dependency property definition (e.g. true or false).
[ViewToViewModel(MappingType = ViewToViewModelMappingType.ViewModelToView)]
public bool IsTurnedOn
{
get { return (bool)GetValue(IsTurnedOnProperty); }
set { SetValue(IsTurnedOnProperty, value); }
}
public static readonly DependencyProperty IsTurnedOnProperty =
DependencyProperty.Register("IsTurnedOn", typeof(bool), typeof(ToggleButton), new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnIsTurnedOnChanged));
private static void OnIsTurnedOnChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var btn = d as ToggleButton;
var vm = btn.ViewModel as ToggleButtonViewModel;
if(vm.IsTurnedOn != btn.IsTurnedOn)
vm.IsTurnedOn = btn.IsTurnedOn;
}
I want to create a TextBox that can take measurement and convert it to different units if necessary (the end result being of type double). The conversion will be controlled by a value IsMetric. If IsMetric == true then "36.5 in" would turn into 927.1 (a double representing millimeters). Conversely, if IsMetric == false then "927.1 mm" would turn into 36.5.
I thought to use an IValueConverter on a regular TextBox, but the ConverterParameter is not a DependencyProperty and therefore I can't bind IsMetric to it.
I tried IMultiValueConverter but the ConvertBack function only receives the current value of the TextBox and not all the bound values. This means I don't know IsMetric when converting the user input.
Have I missed something with the ConvertBack function? If not, then do I need to create a class derived from TextBox?
You could use two converters one to convert from Metric and another to Metric:
public class ToMetricConverter:IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return "(metric) value";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class FromMetricConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return "(Inch) value";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
And use a DataTrigger in the UI to select the appropriate converter based on that bool value:
<Window.Resources>
<wpfApplication13:ToMetricConverter x:Key="ToMetricConverter"/>
<wpfApplication13:FromMetricConverter x:Key="FromMetricConverter"/>
</Window.Resources>
<Grid>
<StackPanel>
<CheckBox IsChecked="{Binding IsMetric,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"></CheckBox>
<TextBox >
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<DataTrigger Binding="{Binding IsMetric,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Value="True">
<Setter Property="Text" Value="{Binding Val,Converter={StaticResource ToMetricConverter}}"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding IsMetric,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Value="False">
<Setter Property="Text" Value="{Binding Val,Converter={StaticResource FromMetricConverter}}"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</StackPanel>
</Grid>
If thats the only thing you want to do, try other way to use converter parameter.
But, and i would have choose this option - if your textbox has more logics in it, or tend to have more dependecie - Create custom control that inherits from textbox, and add your own dependecy properties. Then you can use your IsMetric and convert it as you want on propertychanged etc.
I ended up with something along these lines for now. Would still enjoy a solution that doesn't require a DataTrigger for every possible value.
It's a bit different than the answer posted by #SamTheDev but along the same lines.
xaml
<UserControl x:Class="MyNamespace.Controls.MeasurementTextBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:MyNamespace.Converters"
xmlns:b="clr-namespace:MyNamespace.Behaviors"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
x:Name="root">
<UserControl.Resources>
<c:MeasurementUnitConverter x:Key="muc"/>
<c:MeasurementConverter2 x:Key="mc"/>
<sys:Boolean x:Key="BooleanFalse">False</sys:Boolean>
<sys:Boolean x:Key="BooleanTrue">True</sys:Boolean>
</UserControl.Resources>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="30"/>
</Grid.ColumnDefinitions>
<TextBox Margin="0" VerticalContentAlignment="Center" HorizontalAlignment="Stretch" HorizontalContentAlignment="Right" VerticalAlignment="Stretch"
b:AutoSelectBehavior.AutoSelect="True">
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding UseMetric, ElementName=root}" Value="True">
<Setter Property="Text" Value="{Binding Measurement, Mode=TwoWay, ElementName=root, Converter={StaticResource mc}, ConverterParameter={StaticResource BooleanTrue}}"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding UseMetric, ElementName=root}" Value="False">
<Setter Property="Text" Value="{Binding Measurement, Mode=TwoWay, ElementName=root, Converter={StaticResource mc}, ConverterParameter={StaticResource BooleanFalse}}"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
<!-- in or mm label -->
<Label VerticalAlignment="Center" Padding="0" Margin="5" HorizontalAlignment="Left" Grid.Column="1"
Content="{Binding UseMetric, ElementName=root, Converter={StaticResource muc}}"/>
</Grid>
</UserControl>
xaml.cs
using System;
using System.Windows;
using System.Windows.Controls;
namespace MyNamespace.Controls
{
/// <summary>
/// Interaction logic for MeasurementTextBox.xaml
/// </summary>
public partial class MeasurementTextBox : UserControl
{
public MeasurementTextBox()
{
// This call is required by the designer.
InitializeComponent();
}
public bool UseMetric {
get { return Convert.ToBoolean(GetValue(UseMetricProperty)); }
set { SetValue(UseMetricProperty, value); }
}
public static readonly DependencyProperty UseMetricProperty = DependencyProperty.Register("UseMetric", typeof(bool), typeof(MeasurementTextBox), new PropertyMetadata(MeasurementTextBox.UseMetricChanged));
private static void UseMetricChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
public double Measurement {
get { return (double)GetValue(MeasurementProperty); }
set { SetValue(MeasurementProperty, value); }
}
public static readonly DependencyProperty MeasurementProperty = DependencyProperty.Register("Measurement", typeof(double), typeof(MeasurementTextBox), new PropertyMetadata(MeasurementTextBox.MeasurementPropertyChanged));
private static void MeasurementPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
}
}
Converter
using System;
using System.Windows;
using System.Windows.Data;
namespace MyNamespace.Converters
{
class MeasurementConverter : IValueConverter
{
const double MILLIMETERS_IN_ONE_INCH = 25.4;
const string INCHES_ABBREVIATION = "in";
const string MILLIMETERS_ABBREVIATION = "mm";
const double ONE_THIRTY_SECOND = 0.03125;
const double ONE_SIXTEENTH = 0.0625;
const double ONE_EIGHTH = 0.125;
const double ONE_FOURTH = 0.25;
const double ONE_HALF = 0.5;
const double ONE = 1;
public double RoundToNearest(double value, int unitPrecision)
{
double fraction = 0;
int reciprocal = 0;
switch (unitPrecision)
{
case 0:
fraction = ONE;
reciprocal = (int)ONE;
break;
case 1:
fraction = ONE;
reciprocal = (int)ONE;
break;
case 2:
fraction = ONE_HALF;
reciprocal = (int)(1 / ONE_HALF);
break;
case 3:
fraction = ONE_FOURTH;
reciprocal = (int)(1 / ONE_FOURTH);
break;
case 4:
fraction = ONE_EIGHTH;
reciprocal = (int)(1 / ONE_EIGHTH);
break;
case 5:
fraction = ONE_SIXTEENTH;
reciprocal = (int)(1 / ONE_SIXTEENTH);
break;
case 6:
fraction = ONE_THIRTY_SECOND;
reciprocal = (int)(1 / ONE_THIRTY_SECOND);
break;
}
return Math.Round(value * reciprocal, MidpointRounding.AwayFromZero) * fraction;
}
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string strValue = (string)value;
bool isMetric = (bool)parameter;
double enteredValue = 0;
bool enteredValueIsImperial = false;
if (strValue.EndsWith(INCHES_ABBREVIATION))
{
enteredValueIsImperial = true;
strValue = strValue.Substring(0, strValue.Length - INCHES_ABBREVIATION.Length);
}
else if (strValue.EndsWith(MILLIMETERS_ABBREVIATION))
{
enteredValueIsImperial = false;
strValue = strValue.Substring(0, strValue.Length - MILLIMETERS_ABBREVIATION.Length);
}
else if (isMetric)
{
enteredValueIsImperial = false;
}
else
{
enteredValueIsImperial = true;
}
try
{
enteredValue = double.Parse(strValue);
}
catch (FormatException)
{
return DependencyProperty.UnsetValue;
}
if (isMetric)
{
if (enteredValueIsImperial)
{
//inches to mm
return RoundToNearest(enteredValue * MILLIMETERS_IN_ONE_INCH, 0);
//0 is mm
}
else
{
//mm to mm
return RoundToNearest(enteredValue, 0);
//0 is mm
}
}
else
{
if (enteredValueIsImperial)
{
//inches to inches
return RoundToNearest(enteredValue, 5);
}
else
{
//mm to inches
return RoundToNearest(enteredValue / MILLIMETERS_IN_ONE_INCH, 5);
}
}
}
}
}
Usage:
<mynamespace:MeasurementTextBox Measurement="{Binding SomeLength, Mode=TwoWay}"
UseMetric="{Binding IsMetric}"/>
I'm trying to bind the background of a Tile in a RadTileList, Tiles are created from a collection on the Itemsource of the RadTileList, so far I've tried changing the background on the border container in the Datatemplate, but it seems that the Tile background property is wining over that.
In the code above , I've tried to set the ItemContainerStyle and set the binding for the background, but nothing changes, I hope someone could help me.
Note: The color of the background is a string var so im using a converter, wich I tested independently
<telerik:RadTileList ItemsSource="{Binding Modulo.Modulos_Detail}" TileReorderMode="None"
ScrollViewer.HorizontalScrollBarVisibility="Visible">
<telerik:RadTileList.ItemContainerStyle>
<Style >
<Setter Property="telerik:Tile.TileType" Value="Quadruple" />
<Setter Property="telerik:Tile.Background" Value="{Binding .Color, Converter={StaticResource strHexColorConverter}}" />
</Style>
</telerik:RadTileList.ItemContainerStyle>
<telerik:RadTileList.ItemTemplate>
<DataTemplate>
<Border >
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Image Grid.Row="0"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Source="{Binding .Imagenes.Imagen}" Stretch="Uniform"></Image>
<TextBlock Grid.Row="1" Padding="5"
Text="{Binding Descripcion}" FontSize="20" TextWrapping="Wrap"
VerticalAlignment="Bottom"/>
<!--<Image Source="{Binding .LockViewImage, Converter={StaticResource imgBitmapImageConverter}}" />-->
</Grid>
</Border>
</DataTemplate>
</telerik:RadTileList.ItemTemplate>
Create a seperate style with the Tile as target type. Then you only need to set the Background property. Optionally you could give the style a name and set it explicitly on the RadTileList.
<UserControl.Resources>
<ResourceDictionary>
<Style TargetType="telerik:Tile" BasedOn="{StaticResource TileStyle}">
<Setter Property="Background" Value="{StaticResource OmegaMainColorBrush}" />
</Style>
</ResourceDictionary>
</UserControl.Resources>
In my opinion you can use a Boolean Attached property in the telerik:Tile control style. If that property is True then you create the binding in code behind. The only one thing you should care about is that the Content of the Tile will hold the the object that the .Color is defined there.
Here is the code.
Style (put this into the Resource part)
<Style TargetType="telerik:Tile" BasedOn="{StaticResource {x:Type telerik:Tile}}">
<Setter Property="flowConfiguration:TileAttachedProperties.IsTyleTypeBound" Value="True"/>
</Setter>
The attached property code(with converters)
public class TileAttachedProperties
{
public static readonly DependencyProperty IsTyleTypeBoundProperty = DependencyProperty.RegisterAttached(
"IsTyleTypeBound",
typeof (bool),
typeof (TileAttachedProperties),
new PropertyMetadata(default(bool), IsTyleBoundPropertyChangedCallback));
private static void IsTyleBoundPropertyChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
var tile = sender as Tile;
var isBound = (bool) args.NewValue;
if(tile == null || isBound == false) return;
tile.Loaded += TileOnLoaded;
}
private static void TileOnLoaded(object sender, RoutedEventArgs routedEventArgs)
{
var tile = sender as Tile;
if (tile == null) return;
tile.Loaded -= TileOnLoaded;
var tileContent = tile.Content;
if (tileContent == null || tileContent is ItemTypeWrapper == false) return;
//here we create binding to define if the type of the Tile(single or double)
var tileTypeBinding = new Binding("IsDouble");
tileTypeBinding.Source = tileContent;
tileTypeBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
tileTypeBinding.Converter = new Bool2TileTypeConverter();
tile.SetBinding(Tile.TileTypeProperty, tileTypeBinding);
//here we create binding to define the background of the tile
var tileBackgroundBinding = new Binding("IsDouble");
tileBackgroundBinding.Source = tileContent;
tileBackgroundBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
tileBackgroundBinding.Converter = new Bool2BackgroundConverter();
tile.SetBinding(Tile.BackgroundProperty, tileBackgroundBinding);
}
public static void SetIsTyleTypeBound(DependencyObject element, bool value)
{
element.SetValue(IsTyleTypeBoundProperty, value);
}
public static bool GetIsTyleTypeBound(DependencyObject element)
{
return (bool) element.GetValue(IsTyleTypeBoundProperty);
}
}
internal class Bool2BackgroundConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var isDouble = (bool)value;
return isDouble ? new SolidColorBrush(Color.FromArgb(255, 255, 0, 255)) : new SolidColorBrush(Color.FromArgb(255,0, 255, 255));
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
internal class Bool2TileTypeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var isDouble = (bool) value;
return isDouble ? TileType.Double : TileType.Single;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
How it looks like:
Regards,
I have the following XAML on a ToolBar:
<emsprim:SplitButton Mode="Split">
<emsprim:SplitButton.Content>
<Image Source="images/16x16/Full Extent 1.png" />
</emsprim:SplitButton.Content>
<emsprim:SplitButton.ContextMenu>
<ContextMenu ItemsSource="{Binding CommandGroups[ZoomToDefinedExtentsCmds]}">
<ContextMenu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Command" Value="{Binding Command}" />
<Setter Property="CommandParameter" Value="{Binding ViewID}" />
<Setter Property="Header" Value="{Binding Name}" />
<Setter Property="Icon" Value="{Binding Icon}" />
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</emsprim:SplitButton.ContextMenu>
</emsprim:SplitButton>
where CommandGroups[ZoomToDefinedExtentsCmds] is an IEnumerable of CommandViewModels. Problem is, when I click on the button, I do not see the list of menu items. However, if I bind the same Datacontext to a Menu, like this:
<MenuItem ItemsSource="{Binding CommandGroups[ZoomToDefinedExtentsCmds]}"
Header="Zoom To"
Margin="5,1,5,0" >
<MenuItem.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Command" Value="{Binding Command}" />
<Setter Property="CommandParameter" Value="{Binding CommandParameter}" />
<Setter Property="Header" Value="{Binding Name}" />
<Setter Property="Icon" Value="{Binding Icon}" />
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
I do get the list of MenuItems. Any ideas on what is going on here as there is no binding error in the output VS window. BTW, code for SplitButton is listed below:
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Markup;
using System.Diagnostics;
namespace Controls.Dictionary.Primitives
{
/// <summary>
/// Implemetation of a Split Button
/// </summary>
[TemplatePart(Name = "PART_DropDown", Type = typeof(Button))]
[ContentProperty("Items")]
[DefaultProperty("Items")]
public class SplitButton : Button
{
// AddOwner Dependency properties
public static readonly DependencyProperty PlacementProperty;
public static readonly DependencyProperty PlacementRectangleProperty;
public static readonly DependencyProperty HorizontalOffsetProperty;
public static readonly DependencyProperty VerticalOffsetProperty;
/// <summary>
/// Static Constructor
/// </summary>
static SplitButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(SplitButton), new FrameworkPropertyMetadata(typeof(SplitButton)));
// AddOwner properties from the ContextMenuService class, we need callbacks from these properties
// to update the Buttons ContextMenu properties
PlacementProperty = ContextMenuService.PlacementProperty.AddOwner(typeof(SplitButton), new FrameworkPropertyMetadata(PlacementMode.MousePoint, OnPlacementChanged));
PlacementRectangleProperty = ContextMenuService.PlacementRectangleProperty.AddOwner(typeof(SplitButton), new FrameworkPropertyMetadata(Rect.Empty, OnPlacementRectangleChanged));
HorizontalOffsetProperty = ContextMenuService.HorizontalOffsetProperty.AddOwner(typeof(SplitButton), new FrameworkPropertyMetadata(0.0, OnHorizontalOffsetChanged));
VerticalOffsetProperty = ContextMenuService.VerticalOffsetProperty.AddOwner(typeof(SplitButton), new FrameworkPropertyMetadata(0.0, OnVerticalOffsetChanged));
}
/*
* Properties
*
*/
/// <summary>
/// The Split Button's Items property maps to the base classes ContextMenu.Items property
/// </summary>
public ItemCollection Items
{
get
{
EnsureContextMenuIsValid();
return this.ContextMenu.Items;
}
}
/*
* Dependancy Properties & Callbacks
*
*/
/// <summary>
/// Placement of the Context menu
/// </summary>
public PlacementMode Placement
{
get { return (PlacementMode)GetValue(PlacementProperty); }
set { SetValue(PlacementProperty, value); }
}
/// <summary>
/// Placement Property changed callback, pass the value through to the buttons context menu
/// </summary>
private static void OnPlacementChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
SplitButton s = d as SplitButton;
if (s == null) return;
s.EnsureContextMenuIsValid();
s.ContextMenu.Placement = (PlacementMode)e.NewValue;
}
/// <summary>
/// PlacementRectangle of the Context menu
/// </summary>
public Rect PlacementRectangle
{
get { return (Rect)GetValue(PlacementRectangleProperty); }
set { SetValue(PlacementRectangleProperty, value); }
}
/// <summary>
/// PlacementRectangle Property changed callback, pass the value through to the buttons context menu
/// </summary>
private static void OnPlacementRectangleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
SplitButton s = d as SplitButton;
if (s == null) return;
s.EnsureContextMenuIsValid();
s.ContextMenu.PlacementRectangle = (Rect)e.NewValue;
}
/// <summary>
/// HorizontalOffset of the Context menu
/// </summary>
public double HorizontalOffset
{
get { return (double)GetValue(HorizontalOffsetProperty); }
set { SetValue(HorizontalOffsetProperty, value); }
}
/// <summary>
/// HorizontalOffset Property changed callback, pass the value through to the buttons context menu
/// </summary>
private static void OnHorizontalOffsetChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
SplitButton s = d as SplitButton;
if (s == null) return;
s.EnsureContextMenuIsValid();
s.ContextMenu.HorizontalOffset = (double)e.NewValue;
}
/// <summary>
/// VerticalOffset of the Context menu
/// </summary>
public double VerticalOffset
{
get { return (double)GetValue(VerticalOffsetProperty); }
set { SetValue(VerticalOffsetProperty, value); }
}
/// <summary>
/// VerticalOffset Property changed callback, pass the value through to the buttons context menu
/// </summary>
private static void OnVerticalOffsetChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
SplitButton s = d as SplitButton;
if (s == null) return;
s.EnsureContextMenuIsValid();
s.ContextMenu.VerticalOffset = (double)e.NewValue;
}
/// <summary>
/// Defines the Mode of operation of the Button
/// </summary>
/// <remarks>
/// The SplitButton two Modes are
/// Split (default), - the button has two parts, a normal button and a dropdown which exposes the ContextMenu
/// Dropdown - the button acts like a combobox, clicking anywhere on the button opens the Context Menu
/// </remarks>
public SplitButtonMode Mode
{
get { return (SplitButtonMode)GetValue(ModeProperty); }
set { SetValue(ModeProperty, value); }
}
public static readonly DependencyProperty ModeProperty = DependencyProperty.Register("Mode", typeof(SplitButtonMode), typeof(SplitButton), new FrameworkPropertyMetadata(SplitButtonMode.Split));
/*
* Methods
*
*/
/// <summary>
/// OnApplyTemplate override, set up the click event for the dropdown if present in the template
/// </summary>
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
// set up the event handlers
ButtonBase dropDown = this.Template.FindName("PART_DropDown", this) as ButtonBase;
if (dropDown != null)
dropDown.Click += DoDropdownClick;
}
/// <summary>
/// Make sure the Context menu is not null
/// </summary>
private void EnsureContextMenuIsValid()
{
if (ContextMenu == null)
ContextMenu = new ContextMenu();
}
/*
* Events
*
*/
/// <summary>
/// Event Handler for the Drop Down Button's Click event
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void DoDropdownClick(object sender, RoutedEventArgs e)
{
if (Mode == SplitButtonMode.Dropdown)
return;
if (ContextMenu == null || ContextMenu.HasItems == false) return;
ContextMenu.PlacementTarget = this;
ContextMenu.IsOpen = true;
e.Handled = true;
}
}
}
Problem solved by explicitly setting ContextMenu's DataContext.
ContextMenu is not part of visual tree, therefore, does not resolve DataContext of its "parent'- is one gotcha that gets me every time.
The MenuItem object in your second code snippet, is that outside the SplitButton scope? As in, a direct child of the object container that has the CommandGroups property defined?
I ask because the ContextMenu in the first snippet will have a null DataContext, and as such will not be able to see the CommandGroups property.
I had a similar problem about a year ago, unfortunately, the only way I could solve this was to define the ContextMenu in code and inside the Execute method for a Command. Which enabled me to assign the ItemsSource in code.
To debug DataContext (and other Binding related problems like this) you should create yourself a DebugConverter like:
public class DebugConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
This will help you debug a troublesome binding issue by creating the Binding like: {Binding Converter={StaticResource debugConverter}} and by setting a break point on the return value; line.
Is there a way to create a conditional binding in XAML?
Example:
<Window x:Name="Me" DataContext="{Binding ElementName=Me}">
<TextBlock>
<TextBlock.Text>
<SelectBinding Binding="{Binding MyValue}">
<Case Value="Value1" Value="value is 1!">
<Case Value="Value2" Value="value is 2!">
<Case Value="Value3" Value="value is 3!">
</SelectBinding >
</TextBlock.Text>
</TextBlock>
</Window>
Bottom line, I want to set a TextBlock value according to another value of Binding, that can be of a list of cases where each case (or cases) is addressed to the appropriate output/setter.
Maybe I can use a DataTrigger in my case, I just don't know exactly how I am gonna do it, since I am not using any DataTemplate here.
Update
In my scenario, I am having a UserControl that has several controls.
I want that according to a certain property in the UserControl.DataContext data-item, other controls in the user control should get affected accordingly. Basically same as my example above just that each case leads to a list of Setters.
use a DataTrigger
(EDITED - original had slight mistake)
<TextBlock>
<TextBlock.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding MyValue}" Value="Value1">
<Setter Property="TextBlock.Text" Value="value is 1!"/>
</DataTrigger>
<DataTrigger Binding="{Binding MyValue}" Value="Value2">
<Setter Property="TextBlock.Text" Value="value is 2!"/>
</DataTrigger>
<DataTrigger Binding="{Binding MyValue}" Value="Value3">
<Setter Property="TextBlock.Text" Value="value is 3!"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
You have a number of options...
You could use a 'DataTrigger' by applying a 'Style' to your text block (use 'Style.Triggers').
You could create a converter that would convert your 'MyValue' into the appropriate text.
You could create another property on whatever your data source is (ideally this would be a ViewModel-style class) that reflects the text that should be displayed. Update the property from code and bind directly to it, instead of putting the logic in XAML.
Really I'd see this as a stylistic/design choice - none of the above are inherently better or worse, they're just suited to different scenarios.
Try to use the Switch Converter written by Josh:
SwitchConverter –
A "switch statement" for XAML -
http://josheinstein.com/blog/index.php/2010/06/switchconverter-a-switch-statement-for-xaml/
Edit:
Here is code of SwitchConverter as Josh's site seems to be down -
/// <summary>
/// A converter that accepts <see cref="SwitchConverterCase"/>s and converts them to the
/// Then property of the case.
/// </summary>
[ContentProperty("Cases")]
public class SwitchConverter : IValueConverter
{
// Converter instances.
List<SwitchConverterCase> _cases;
#region Public Properties.
/// <summary>
/// Gets or sets an array of <see cref="SwitchConverterCase"/>s that this converter can use to produde values from.
/// </summary>
public List<SwitchConverterCase> Cases { get { return _cases; } set { _cases = value; } }
#endregion
#region Construction.
/// <summary>
/// Initializes a new instance of the <see cref="SwitchConverter"/> class.
/// </summary>
public SwitchConverter()
{
// Create the cases array.
_cases = new List<SwitchConverterCase>();
}
#endregion
/// <summary>
/// Converts a value.
/// </summary>
/// <param name="value">The value produced by the binding source.</param>
/// <param name="targetType">The type of the binding target property.</param>
/// <param name="parameter">The converter parameter to use.</param>
/// <param name="culture">The culture to use in the converter.</param>
/// <returns>
/// A converted value. If the method returns null, the valid null value is used.
/// </returns>
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
// This will be the results of the operation.
object results = null;
// I'm only willing to convert SwitchConverterCases in this converter and no nulls!
if (value == null) throw new ArgumentNullException("value");
// I need to find out if the case that matches this value actually exists in this converters cases collection.
if (_cases != null && _cases.Count > 0)
for (int i = 0; i < _cases.Count; i++)
{
// Get a reference to this case.
SwitchConverterCase targetCase = _cases[i];
// Check to see if the value is the cases When parameter.
if (value == targetCase || value.ToString().ToUpper() == targetCase.When.ToString().ToUpper())
{
// We've got what we want, the results can now be set to the Then property
// of the case we're on.
results = targetCase.Then;
// All done, get out of the loop.
break;
}
}
// return the results.
return results;
}
/// <summary>
/// Converts a value.
/// </summary>
/// <param name="value">The value that is produced by the binding target.</param>
/// <param name="targetType">The type to convert to.</param>
/// <param name="parameter">The converter parameter to use.</param>
/// <param name="culture">The culture to use in the converter.</param>
/// <returns>
/// A converted value. If the method returns null, the valid null value is used.
/// </returns>
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
/// <summary>
/// Represents a case for a switch converter.
/// </summary>
[ContentProperty("Then")]
public class SwitchConverterCase
{
// case instances.
string _when;
object _then;
#region Public Properties.
/// <summary>
/// Gets or sets the condition of the case.
/// </summary>
public string When { get { return _when; } set { _when = value; } }
/// <summary>
/// Gets or sets the results of this case when run through a <see cref="SwitchConverter"/>
/// </summary>
public object Then { get { return _then; } set { _then = value; } }
#endregion
#region Construction.
/// <summary>
/// Switches the converter.
/// </summary>
public SwitchConverterCase()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="SwitchConverterCase"/> class.
/// </summary>
/// <param name="when">The condition of the case.</param>
/// <param name="then">The results of this case when run through a <see cref="SwitchConverter"/>.</param>
public SwitchConverterCase(string when, object then)
{
// Hook up the instances.
this._then = then;
this._when = when;
}
#endregion
/// <summary>
/// Returns a <see cref="System.String"/> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="System.String"/> that represents this instance.
/// </returns>
public override string ToString()
{
return string.Format("When={0}; Then={1}", When.ToString(), Then.ToString());
}
}
I made an simplified, updated converter based on the accepted answer.
It also allows a string comparison and a default case to be set:
[ContentProperty("Cases")]
public class SwitchConverter : IValueConverter
{
public SwitchConverter()
{
Cases = new List<SwitchConverterCase>();
}
public List<SwitchConverterCase> Cases { get; set; }
public StringComparison StringComparisonType { get; set; } = StringComparison.InvariantCulture;
public object Default { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null || Cases == null)
{
return DependencyProperty.UnsetValue;
}
SwitchConverterCase result = Cases.FirstOrDefault(c => string.Equals(value.ToString(), c.When, StringComparisonType));
return result != null ? result.Then : Default;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
The SwitchConverterCase class:
[ContentProperty("Then")]
public class SwitchConverterCase
{
public SwitchConverterCase()
{
}
public SwitchConverterCase(string when, object then)
{
When = when;
Then = then;
}
public string When { get; set; }
public object Then { get; set; }
public override string ToString() => $"When={When}; Then={Then}";
}
Example usage:
<con:SwitchConverter x:Key="StyleConverter"
Default="{x:Static FontWeights.Normal}">
<con:SwitchConverterCase When="pageHeader"
Then="{x:Static FontWeights.Bold}" />
<con:SwitchConverterCase When="header"
Then="{x:Static FontWeights.SemiBold}" />
<con:SwitchConverterCase When="smallText"
Then="{x:Static FontWeights.Light}" />
<con:SwitchConverterCase When="tinyText"
Then="{x:Static FontWeights.Thin}" />
</con:SwitchConverter>
<TextBlock FontWeight="{Binding Style, Converter={StaticResource StyleConverter}}" />
Or inline:
<TextBlock>
<TextBlock.FontWeight>
<Binding Path="Style">
<Binding.Converter>
<con:SwitchConverter Default="{x:Static FontWeights.Normal}">
<con:SwitchConverterCase When="pageHeader"
Then="{x:Static FontWeights.Bold}" />
<!-- etc -->
</con:SwitchConverter>
</Binding.Converter>
</Binding>
</TextBlock.FontWeight>
</TextBlock>
You could just use a converter as Dan suggested...
public class MyValueConverter : IValueConverter
{
public object Convert(
object value,
Type targetType,
object parameter,
System.Globalization.CultureInfo culture)
{
string myValue = value.ToString();
string output;
switch(myValue)
{
case "Value1":
output = "Value is 1";
break;
case "Value2":
output = "Value is 2";
break;
case "Value3":
output = "Value is 3";
break;
default:
output = "Invalid Value";
break;
}
return output;
}
public object ConvertBack(
object value,
Type targetType,
object parameter,
System.Globalization.CultureInfo culture)
{
//Put reverse logic here
throw new NotImplementedException();
}
}
You would then use this from within your xaml...
<TextBlock
Text="{Binding MyValue, Converter={StaticResource MyValueConverter}}"/>