I can set in XAML the icon container:
<Image Source="Shell32.dll.ico" />
But how can I set in XAML the icon index in the container ? something like:
<Image Source="Shell32.dll,5" />
Or like:
<Image Source="Shell32.dll" Index="5" />
etc...
This is how it goes: first the IValueConverter:
using System;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Data;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
[ValueConversion(typeof(string), typeof(ImageSource))]
public class HabeasIcon : IValueConverter
{
[DllImport("shell32.dll")]
private static extern IntPtr ExtractIcon(IntPtr hInst, string lpszExeFileName, int nIconIndex);
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string[] fileName = ((string)parameter).Split('|');
if (targetType != typeof(ImageSource))
return Binding.DoNothing;
IntPtr hIcon = ExtractIcon(Process.GetCurrentProcess().Handle, fileName[0], int.Parse(fileName[1]));
ImageSource ret = Imaging.CreateBitmapSourceFromHIcon(hIcon, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
return ret;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{ throw new NotImplementedException(); }
}
The XAML:
<Image Source="{Binding Converter={StaticResource iconExtractor}, ConverterParameter=c:\\Windows\\System32\\shell32.dll|72}"/>
Related
I have two radio buttons and are binding them to an enum
Like in How to bind RadioButtons to an enum?
namespace RadioButtons
{
public enum WorkModeEnum
{
Auto,
Manual,
}
}
<Window
x:Class="RadioButtons.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:local="clr-namespace:RadioButtons"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow"
Width="800"
Height="450"
ContentRendered="Window_ContentRendered"
mc:Ignorable="d">
<Grid>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<StackPanel.Resources>
<local:EnumToBooleanConverter x:Key="ComparisonConverter" />
</StackPanel.Resources>
<RadioButton Content="Auto" IsChecked="{Binding Path=WorkMode, Mode=TwoWay, Converter={StaticResource ComparisonConverter}, ConverterParameter={x:Static local:WorkModeEnum.Auto}}" />
<RadioButton Content="Manual" IsChecked="{Binding Path=WorkMode, Mode=TwoWay, Converter={StaticResource ComparisonConverter}, ConverterParameter={x:Static local:WorkModeEnum.Manual}}" />
</StackPanel>
</Grid>
</Window>
Code behind
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace RadioButtons
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private ViewModel _vm;
public MainWindow()
{
InitializeComponent();
}
private void Window_ContentRendered(object sender, EventArgs e)
{
_vm = new ViewModel();
DataContext = _vm;
}
}
}
The converter code looks like this
public class EnumToBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return ((Enum)value).HasFlag((Enum)parameter);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value.Equals(true) ? parameter : Binding.DoNothing;
}
}
ViewModel
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace RadioButtons
{
public class ViewModel : INotifyPropertyChanged
{
public ViewModel()
{
WorkMode = WorkModeEnum.Manual;
}
private WorkModeEnum _WorkMode;
public WorkModeEnum WorkMode
{
get { return _WorkMode; }
set
{
_WorkMode = value;
NotifyPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
It works, except for when the user click on the Manual button when it's already selected!
Then both of the buttons will show selected!
Why and how fix it?
The whole project is accessible (hopefully, I have never done this before) at
https://github.com/Andis59/RadioButtons
Your problem is the Converter. You are using a normal Enum but the converter treat it as [Flags] (which are a kind of different).
Enum.HasFlag(flag) will always return true if the ordinal value of the given flag is 0. Your WorkModeEnum.Auto has that ordinal value of 0 and causing this.
You could fix this by changing the WorkModeEnum definition to
public enum WorkModeEnum
{
Auto = 1,
Manual = 2,
}
but it is much better to fix the Converter
public class EnumToBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return ((Enum)parameter).Equals((Enum)value);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value.Equals(true) ? parameter : Binding.DoNothing;
}
}
I want all characters in a TextBlock to be displayed in uppercase
<TextBlock Name="tbAbc"
FontSize="12"
TextAlignment="Center"
Text="Channel Name"
Foreground="{DynamicResource {x:Static r:RibbonSkinResources.RibbonGroupLabelFontColorBrushKey}}" />
The strings are taken through Binding. I don't want to make the strings uppercase in the dictionary itself.
Or use
Typography.Capitals="AllSmallCaps"
in your TextBlock definition.
See here: MSDN - Typography.Capitals
EDIT:
This does not work in Windows Phone 8.1, only in Windows 8.1 ...
Implement a custom converter.
using System.Globalization;
using System.Windows.Data;
// ...
public class StringToUpperConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null && value is string )
{
return ((string)value).ToUpper();
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
Then include that in your XAML as a resource:
<local:StringToUpperConverter x:Key="StringToUpperConverter"/>
And add it to your binding:
Converter={StaticResource StringToUpperConverter}
You can use an attached property like this:
public static class TextBlock
{
public static readonly DependencyProperty CharacterCasingProperty = DependencyProperty.RegisterAttached(
"CharacterCasing",
typeof(CharacterCasing),
typeof(TextBlock),
new FrameworkPropertyMetadata(
CharacterCasing.Normal,
FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.NotDataBindable,
OnCharacterCasingChanged));
private static readonly DependencyProperty TextProxyProperty = DependencyProperty.RegisterAttached(
"TextProxy",
typeof(string),
typeof(TextBlock),
new PropertyMetadata(default(string), OnTextProxyChanged));
private static readonly PropertyPath TextPropertyPath = new PropertyPath("Text");
public static void SetCharacterCasing(DependencyObject element, CharacterCasing value)
{
element.SetValue(CharacterCasingProperty, value);
}
public static CharacterCasing GetCharacterCasing(DependencyObject element)
{
return (CharacterCasing)element.GetValue(CharacterCasingProperty);
}
private static void OnCharacterCasingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is System.Windows.Controls.TextBlock textBlock)
{
if (BindingOperations.GetBinding(textBlock, TextProxyProperty) == null)
{
BindingOperations.SetBinding(
textBlock,
TextProxyProperty,
new Binding
{
Path = TextPropertyPath,
RelativeSource = RelativeSource.Self,
Mode = BindingMode.OneWay,
});
}
}
}
private static void OnTextProxyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
d.SetCurrentValue(System.Windows.Controls.TextBlock.TextProperty, Format((string)e.NewValue, GetCharacterCasing(d)));
string Format(string text, CharacterCasing casing)
{
if (string.IsNullOrEmpty(text))
{
return text;
}
switch (casing)
{
case CharacterCasing.Normal:
return text;
case CharacterCasing.Lower:
return text.ToLower();
case CharacterCasing.Upper:
return text.ToUpper();
default:
throw new ArgumentOutOfRangeException(nameof(casing), casing, null);
}
}
}
}
Then usage in xaml will look like:
<StackPanel>
<TextBox x:Name="TextBox" Text="abc" />
<TextBlock local:TextBlock.CharacterCasing="Upper" Text="abc" />
<TextBlock local:TextBlock.CharacterCasing="Upper" Text="{Binding ElementName=TextBox, Path=Text}" />
<Button local:TextBlock.CharacterCasing="Upper" Content="abc" />
<Button local:TextBlock.CharacterCasing="Upper" Content="{Binding ElementName=TextBox, Path=Text}" />
</StackPanel>
If it's not a big deal you could use TextBox instead of TextBlock like this:
<TextBox CharacterCasing="Upper" IsReadOnly="True" />
While there's already a great answer here that uses a converter, I'm providing an alternative implementation that simplifies the conversion to a single line (thanks to null coalescing), as well as making it a subclass of MarkupExtension so it's easier to use in XAML.
Here's the converter...
using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Markup;
namespace IntuoSoft.Wpf.Converters {
[ValueConversion(typeof(string), typeof(string))]
public class CapitalizationConverter : MarkupExtension, IValueConverter {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
=> (value as string)?.ToUpper() ?? value; // If it's a string, call ToUpper(), otherwise, pass it through as-is.
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
=> throw new NotSupportedException();
public override object ProvideValue(IServiceProvider serviceProvider) => this;
}
}
And here's how you use it (Note: This assumes the above namespace is prefixed with is in your XAML):
<TextBlock Text={Binding SomeValue, Converter={is:CapitalizationConverter}}" />
Because it's a MarkupExtension subclass, you can simply use it right where/when it's needed. No need to define it in the resources first.
I use a character casing value converter:
class CharacterCasingConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var s = value as string;
if (s == null)
return value;
CharacterCasing casing;
if (!Enum.TryParse(parameter as string, out casing))
casing = CharacterCasing.Upper;
switch (casing)
{
case CharacterCasing.Lower:
return s.ToLower(culture);
case CharacterCasing.Upper:
return s.ToUpper(culture);
default:
return s;
}
}
object IValueConverter.ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
We are currently in the process of converting a project from version 3.5 to version 4.5 of .NET.
We set a text box IsEnabled flagged using a multi binding with a multi binding converter. Each of the bindings has their own converter.
All worked well in .NET 3.5 but in .NET 4.5 the target type that is passed to the child converter is of type object instead of bool.
Is this a known issue? has MS refactored the multi binding to not pass the target type to child converters.
I created a simplified project that demonstrates the issue. I created the project in VS2008 and then converted it to VS2012 and .NET 4.5.
Window XAML:
<Window x:Class="TestMultiBinding.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestMultiBinding"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<local:NotConverter x:Key="NotConverter"/>
<local:MultiBoolConverter x:Key="MultiBoolConverter"/>
</Window.Resources>
<StackPanel>
<TextBox>
<TextBox.IsEnabled>
<MultiBinding Converter="{StaticResource MultiBoolConverter}">
<Binding Path="ConditionOne" />
<Binding Path="ConditionTwo" Converter="{StaticResource NotConverter}"/>
</MultiBinding>
</TextBox.IsEnabled>
</TextBox>
</StackPanel>
</Window>
c#:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Globalization;
namespace TestMultiBinding
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
DataContext = new ViewModel();
}
}
public class ViewModel
{
public bool ConditionOne { get { return true; } }
public bool ConditionTwo { get { return false; } }
}
/// <summary>
/// Converts a boolean to its inverse (useful for radio buttons).
/// </summary>
[ValueConversion(typeof(bool), typeof(bool))]
public class NotConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (targetType != typeof(bool) && targetType != typeof(bool?)) { throw new ArgumentException("Can only convert booleans.", "targetType"); }
//return !(bool)value;
return !true.Equals(value);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Convert(value, targetType, parameter, culture);
}
}
/// <summary>
/// Converts multiple boolean values to one. Uses AND by default. Possible extension: Pass the desired operation as parameter
/// </summary>
[ValueConversion(typeof(bool), typeof(bool))]
public class MultiBoolConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
try
{
// todo: support other operations like OR, XOR
return values.Cast<bool>().Aggregate(true, (res, cur) => res && cur);
}
catch (Exception ex)
{
System.Diagnostics.Trace.TraceError("MultiBoolConverter({0}): {1}", parameter, ex.Message);
return DependencyProperty.UnsetValue;
}
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
System.Diagnostics.Trace.TraceError("MultiBoolConverter: does not support TwoWay or OneWayToSource bindings.");
return null;
}
}
}
Is there a reason you are testing that the targetType is bool?
I'm surprised it worked in 3.5, as the NonConverter is converting from bool to object (as the MultiBinding takes an array of object as it's inout).
I did some digging using reflector, and the underlying logic did change.
This is from the internal void TransferValue(object newValue, bool isASubPropertyChange) method of BindingExpression
In 3.5:
internal void TransferValue(object newValue, bool isASubPropertyChange)
{
DependencyObject targetElement = this.TargetElement;
if (targetElement == null || this.Worker == null)
return;
Type propertyType = this.TargetProperty.PropertyType;
In 4.5, all calls to propertyType are replaced by the below definition of effectiveTargetType:
internal void TransferValue(object newValue, bool isASubPropertyChange)
{
DependencyObject targetElement = this.TargetElement;
if (targetElement == null || this.Worker == null)
return;
Type effectiveTargetType = this.GetEffectiveTargetType();
...
}
internal Type GetEffectiveTargetType()
{
Type type = this.TargetProperty.PropertyType;
for (BindingExpressionBase bindingExpressionBase = this.ParentBindingExpressionBase; bindingExpressionBase != null; bindingExpressionBase = bindingExpressionBase.ParentBindingExpressionBase)
{
if (bindingExpressionBase is MultiBindingExpression)
{
type = typeof (object);
break;
}
}
return type;
}
I'm not sure what TargetProperty is set to in this case, but you can see why it's now being set to object for MultiBindings.
And, FYI, it appears this change occurred in .NET 4.0.
I am trying to make the width of a column in a Grid element variable. For this I have a DependencyProperty "ItemWidth" and bind the Width-element from the Button to this DP. Because of the TwoWay-Binding, I need a converter that converts doubles to DataGridLength.
My MainWindow.xaml looks like this:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:utils="clr-namespace:WpfApplication1" Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.Resources>
<utils:ColumnWidthConverter x:Key="columnWidthConverter"/>
</Grid.Resources>
<Button Grid.Row="0" Grid.Column="0" Width="{Binding Path=ItemWidth, Mode=TwoWay, Converter={StaticResource columnWidthConverter}}" Click="Shorter_Click">shorter</Button>
<Button Grid.Row="0" Grid.Column="1" Width="{Binding Path=ItemWidth, Mode=TwoWay, Converter={StaticResource columnWidthConverter}}" Click="Longer_Click">longer</Button>
</Grid>
</Window>
The ColumnWidthConverter.cs is as follows:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows;
namespace WpfApplication1
{
class ColumnWidthConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null)
return null;
else
{
DataGridLengthConverter cv = new DataGridLengthConverter();
object result = cv.ConvertFrom(value);
return result;
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null)
return null;
else
{
DataGridLengthConverter cv = new DataGridLengthConverter();
return cv.ConvertTo(value, typeof(double));
}
}
}
}
And the MainWindow.xaml.cs looks like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public double ItemWidth
{
get { return (double)GetValue(ItemWidthProperty); }
set { SetValue(ItemWidthProperty, value); }
}
public static readonly DependencyProperty ItemWidthProperty =
DependencyProperty.Register("ItemWidth", typeof(double), typeof(MainWindow), new UIPropertyMetadata(0.0));
private void Shorter_Click(object sender, RoutedEventArgs e)
{
this.ItemWidth -= 100;
}
private void Longer_Click(object sender, RoutedEventArgs e)
{
this.ItemWidth += 100;
}
}
}
So the Width of the Buttons should change when I click one of the Buttons. But this does not happen. Can you tell me why that is and some sort of solution?
You set no source in the binding, hence it is relative to the DataContext, which you do not appear to have set anywhere, if you add it that should work. e.g.
<Window DataContext="{Binding RelativeSource={RelativeSource Self}}" ...
You might want to (i.e. you definitely should) look into debugging data bindings if you are not familiar with it.
You also bind properties which have no need for the converter, in fact the converter will probably cause problems here, did you not mean to bind the ColumnDefinition.Width?
i use the following for my GridSplitter
public class DoubleToGridLengthConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
double i = (double)value;
GridLength result = new GridLength(i);
return result;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
GridLength g = (GridLength)value;
return (double)g.Value;
}
}
xaml
Width="{Binding Source={x:Static Properties:Settings.Default}, Path=GridSplitter, Mode=TwoWay, Converter={StaticResource GridLengthConverter}}"
In Silverlight how do I get the color of a button to change according to the value of its contents .. e.g. '0' = red , '1' = green ..
I have taken a look at the VisualStateManger but cannot see how to do it .. I can see it is easy for mouseovers etc .. but not for values of data.
What you need is a value converter, that is an implementation of IValueConverter. In this blog article you find the code for a StringToObjectConverter which you can use for your task. I'll reproduce the code here:-
using System;
using System.Windows;
using System.Windows.Data;
using System.Linq;
using System.Windows.Markup;
namespace SilverlightApplication1
{
[ContentProperty("Items")]
public class StringToObjectConverter : IValueConverter
{
public ResourceDictionary Items { get; set; }
public string DefaultKey { get; set; }
public StringToObjectConverter()
{
DefaultKey = "__default__";
}
public virtual object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value != null && Items.Contains(value.ToString()))
return Items[value.ToString()];
else
return Items[DefaultKey];
}
public virtual object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return Items.FirstOrDefault(kvp => value.Equals(kvp.Value)).Key;
}
}
}
Now you can add an instance of this converter to the resources in you your user control:-
<UserControl.Resources>
<local:StringToObjectConverter x:Key="StatusToBrush">
<ResourceDictionary>
<SolidColorBrush Color="Red" x:Key="0" />
<SolidColorBrush Color="Green" x:Key="1" />
<SolidColorBrush Color="Silver" x:Key="__default__" />
</ResourceDictionary>
</local:StringToObjectConverter>
</UserControl>
Now you can bind the Background to your value:-
<Button Background="{Binding Value, Converter={StaticResource StatusToBrush}}">
<TextBlock Text="{Binding Value}" />
</Button>