I would like to create a type of control that will be able to see a narrowed and expanded list by some button, an expander or just a button, and also display some hidden items
I tried to create a style that would have a ListBox of a certain size (let's say it would be enough for 3 items)
And the Expander or some Button that would be below it would say how many more items there are
And give the option to expand and see everything or reduce and return to see 3 items
What I created here works pretty poorly and doesn't look good.
I would appreciate help on how to improve it or how to do it in a better and more correct way
<Style x:Key="CustomListBoxKey" TargetType="{x:Type ListBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<ItemsPresenter/>
<Expander x:Name="Expander" Grid.Row="1" Header="Show X More" ExpandDirection="Down"/>
</Grid>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding ElementName=Expander, Path=IsExpanded}" Value="False">
<Setter Property="MaxHeight" Value="80"/>
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=Expander, Path=IsExpanded}" Value="True">
<Setter Property="MaxHeight" Value="Infinity"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
public List<string> Items { get; set; } = new() { "A", "B", "C", "D", "E", "F", "G", "H", "I" };
<Grid>
<ListBox ItemsSource="{Binding Items}" Style="{StaticResource CustomListBoxKey}" VerticalAlignment="Top">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=.}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
This is an improvement to your code, and it will give you what you asked for
<local:InvertableBooleanToVisibilityConverter x:Key="InvertableBooleanToVisibilityConverterKey"/>
<local:MathValueConverter x:Key="MathValueConverterKey"/>
<Style x:Key="CustomListBoxKey" TargetType="{x:Type ListBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<ItemsPresenter x:Name="ItemsPresenter"/>
<Expander
x:Name="Expander"
Grid.Row="1"
ExpandDirection="Down">
<Expander.Header>
<StackPanel
Orientation="Horizontal"
Visibility="{Binding RelativeSource={RelativeSource AncestorType=Expander}, Path=IsExpanded, Converter={StaticResource InvertableBooleanToVisibilityConverterKey}}">
<TextBlock Text="Show "/>
<TextBlock
Text="{Binding RelativeSource={RelativeSource Self}, Path=DataContext.Items.Count,
Converter={StaticResource MathValueConverterKey},
ConverterParameter=-2}">
</TextBlock>
<TextBlock Text=" More"/>
</StackPanel>
</Expander.Header>
</Expander>
</Grid>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding ElementName=Expander, Path=IsExpanded}" Value="False">
<Setter Property="MaxHeight" Value="80"/>
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=Expander, Path=IsExpanded}" Value="True">
<Setter Property="MaxHeight" Value="Infinity"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
public class MathValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
int.TryParse(parameter.ToString(), out var x);
return (int)value + x;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class InvertableBooleanToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value is bool and true ? Visibility.Collapsed : Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
I would like to present a solution using a UserControl. Actually we have a request for two slightly different presentation. As a first step I build a UserControl called DualDisplay. The DualDisplay gets : "Short Display" and "LongDisplay" as parameters.
The DualDisplay UserControl is hosting the Expander:
<UserControl.Resources>
<local:InvertBooleanToVisibilityConverter x:Key="InvertBooleanToVisibilityConverter"/>
<Style TargetType="Expander" x:Key="ExpanderHeaders">
<Style.Triggers>
<Trigger Property="IsExpanded" Value="true">
<Setter Property="Header" Value="Show Less"/>
</Trigger>
<Trigger Property="IsExpanded" Value="false">
<Setter Property="Header" Value="Show More"/>
</Trigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
<Grid>
<StackPanel Orientation="Vertical">
<Expander Name="expander" IsExpanded="False" Style="{StaticResource ExpanderHeaders}">
<ContentPresenter Content="{Binding ElementName=ours,Path=LongContent}"/>
</Expander>
<ContentPresenter Content="{Binding ElementName=ours,Path=ShortContent}"
Visibility="{Binding Path=IsExpanded,ElementName=expander,Converter={StaticResource ResourceKey=InvertBooleanToVisibilityConverter}}"/>
</StackPanel>
</Grid>
And the using of the DualDisplay will look as following :
<local:DualDisplay>
<local:DualDisplay.ShortContent>
<ListBox ItemsSource="{Binding Items}" VerticalAlignment="Top" MaxHeight="80">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=.}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</local:DualDisplay.ShortContent>
<local:DualDisplay.LongContent>
<ListBox ItemsSource="{Binding Items}" VerticalAlignment="Top" MaxHeight="1000">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=.}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</local:DualDisplay.LongContent>
</local:DualDisplay>
When we use this method we can reuse the DualDisplay for different cases and we are free to design the short/long display as we wish.
I attach also the code behind of the DualDisplay:
public object ShortContent
{
get { return (object)GetValue(ShortContentProperty); }
set { SetValue(ShortContentProperty, value); }
}
public static readonly DependencyProperty ShortContentProperty =
DependencyProperty.Register("ShortContent", typeof(object), typeof(DualDisplay),
new PropertyMetadata(null));
public object LongContent
{
get { return (object)GetValue(LongContentProperty); }
set { SetValue(LongContentProperty, value); }
}
public static readonly DependencyProperty LongContentProperty =
DependencyProperty.Register("LongContent", typeof(object), typeof(DualDisplay),
new PropertyMetadata(null));
Related
I'm trying to change the background colour of a text box in WPF based on its content.
Using breakpoints, I see my ValueConverter gets constructed, but neither Convert nor ConvertBack methods ever get called, and so the styling doesn't work.
The 'LightBlue' style in the XAML does work at startup.
I tried to use Snoop but I don't know what I'm looking for.
XAML...
<Grid.Resources>
<local:ThreadCreationLimitChanged x:Key="ThreadCreationLimitChanged"></local:ThreadCreationLimitChanged>
</Grid.Resources>
<Label Grid.Row="0" Grid.Column="0" Margin="0, 5, 0, 0" Content="New Thread Limit"></Label>
<TextBox Grid.Row="0" Grid.Column="1" Margin="10, 5, 10, 0" Width="100" Text="{Binding Path=ManagerConfig.ThreadCreationLimit, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" >
<TextBox.Style>
<Style>
<Setter Property="TextBox.Background" Value="LightBlue"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Text,Converter={StaticResource ThreadCreationLimitChanged}}" Value="false">
<Setter Property="TextBox.Background" Value="Yellow"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
ValueConverter...
public class ThreadCreationLimitChanged : IValueConverter
{
public ThreadCreationLimitChanged()
{
}
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value.ToString() == "120";
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}
A bit of class ManagerConfig... (NotifyPropertyChanged implements INotifyPropertyChanged)
class ManagerConfig : NotifyPropertyChanged
{
private uint _ThreadCreationLimit;
public uint ThreadCreationLimit
{
get { return _ThreadCreationLimit; }
set
{
_ThreadCreationLimit = value;
OnPropertyChanged("ThreadCreationLimit");
}
}
Why not create a computed property on ManagerConfig which reports a status for the change of the ThreadCreationLimit? That way one doesn't need a converter. Code such as this:
public bool IsOverLimit { get { return ThreadCreationLimit > 120 } }
public uint ThreadCreationLimit
{
get { return _ThreadCreationLimit; }
set
{
_ThreadCreationLimit = value;
OnPropertyChanged("ThreadCreationLimit");
OnPropertyChanged("IsOverLimit");
}
}
Then bind the data trigger to IsOverLimit which will have a change notification sent everytime ThreadCreationLimit is set/changed.
An added plus is that the business logic is on the intended class and not a converter.
Change this {Binding Path=Text,Converter={StaticResource ThreadCreationLimitChanged}} to {Binding Path=Text, RelativeSource={RelativeSource Self} ,Converter={StaticResource ThreadCreationLimitChanged}}
I guess you are having Binding errors.Have checked like below,
1) Accessing TextBox directly for Value,
<Grid.Resources>
<local:ThreadCreationLimitChanged x:Key="ThreadCreationLimitChanged"></local:ThreadCreationLimitChanged>
</Grid.Resources>
<Label Grid.Row="0" Grid.Column="0" Margin="0, 5, 0, 0" Content="New Thread Limit"></Label>
<TextBox x:Name="tBox" Grid.Row="0" Grid.Column="1" Margin="10, 5, 10, 0" Width="100" Text="{Binding Path=ManagerConfig.ThreadCreationLimit, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" >
<TextBox.Style>
<Style>
<Setter Property="TextBox.Background" Value="LightBlue"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Text, ElementName=tBox,Converter={StaticResource ThreadCreationLimitChanged}}" Value="false">
<Setter Property="TextBox.Background" Value="Yellow"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
2) Accesing proper Model values,
<Grid.Resources>
<local:ThreadCreationLimitChanged x:Key="ThreadCreationLimitChanged"></local:ThreadCreationLimitChanged>
</Grid.Resources>
<Label Grid.Row="0" Grid.Column="0" Margin="0, 5, 0, 0" Content="New Thread Limit"></Label>
<TextBox Grid.Row="0" Grid.Column="1" Margin="10, 5, 10, 0" Width="100" Text="{Binding Path=ManagerConfig.ThreadCreationLimit, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" >
<TextBox.Style>
<Style>
<Setter Property="TextBox.Background" Value="LightBlue"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=ManagerConfig.ThreadCreationLimit,Converter={StaticResource ThreadCreationLimitChanged}}" Value="false">
<Setter Property="TextBox.Background" Value="Yellow"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
So i have this custom Progress-bar that i put inside ListView column:
<Style x:Key="CustomProgressBar" TargetType="ProgressBar" >
<Setter Property="Template" >
<Setter.Value>
<ControlTemplate TargetType="ProgressBar">
<Border BorderBrush="Gray" BorderThickness="1" Background="Gray" CornerRadius="0" Padding="0" >
<Grid x:Name="PART_Track">
<Rectangle x:Name="PART_Indicator" HorizontalAlignment="Left" Fill="MediumSeaGreen" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
ListView
<ListView.Resources>
<DataTemplate x:Key="MyDataTemplate">
<Grid Margin="-6" Height="22">
<ProgressBar
Name="progressBarColumn"
Maximum="100"
Value="{Binding Progress, UpdateSourceTrigger=PropertyChanged}"
Width="{Binding Path=Width, ElementName=ProgressCell}"
Margin="0"
Style="{StaticResource CustomProgressBar}" />
<TextBlock
Text="{Binding Path=Value, ElementName=progressBarColumn, StringFormat={}{0:N1}%}"
VerticalAlignment="Center"
HorizontalAlignment="Center"
FontSize="11.5"
Foreground="Gainsboro"
Margin="0,1,0,0"/>
</Grid>
</DataTemplate>
</ListView.Resources>
So as you can see my Progress-bar color is Gray and while start to fill the color become MediumSeaGreen.
So i want the color will be Gray and while start to fill i want this color become Yellow and only when its value reach 100% i want it to be MediumSeaGreen.
Is it possible ?
All you need is an implementation for the IValueConverter interface such as:
public class ProgressToColorConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
var progress = (double)value;
//your coloring conditions here, just return the color based on the progress value
if (progress == 100d)
{
return Brushes.MediumSeaGreen;
}
if (progress >= 0.01d)
{
return Brushes.Yellow;
}
return Brushes.Gray;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
then add it to your your resources:
<Window.Resources>
<local:ProgressToColorConverter x:Key="ProgressToColorConverter"/>
</Window.Resources>
and use it in your progress bar as
<ProgressBar ...
Foreground="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Value, Converter={StaticResource ProgressToColorConverter}}"/>
it is possible. use a Style on rectanle with default setter for Yellow color and change it to MediumSeaGreen via trigger when ProgressBar.Value reaches 100
<Rectangle x:Name="PART_Indicator" HorizontalAlignment="Left">
<Rectangle.Style>
<Style TargetType="Rectangle">
<Setter Property="Fill" Value="Yellow"/>
<Style.Triggers>
<DataTrigger Value="100" Binding="{Binding Path=Value, RelativeSource={RelativeSource AncestorType=ProgressBar}}">
<Setter Property="Fill" Value="MediumSeaGreen"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
I want to add the check box for treeview of observable collection. the following code add the check box for all the parent and children nodes. I need only for the parent node Is there any way to achieve this...
<HierarchicalDataTemplate x:Key="LogFolderExplorer" DataType="{x:Type TestAutomationClient:TestArtifact}" ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal" MinWidth="200">
<CheckBox
Focusable="False"
IsChecked="{Binding IsChecked}"
VerticalAlignment="Center"
/>
<TextBlock Text="{Binding Name}" FontSize="14"/>
</StackPanel>
</HierarchicalDataTemplate>
There are tons of ways you can achieve this.
One of them:
You can go to the ViewModel for the TestAutomationClient:TestArtifact, and add the bool property here:
public bool IsEmpty { get { return !this.Children.Any() } }
You should update it when the entire collection has changed (if this is your case), i.e. you should make RaisePropertyChanged("IsEmpty"). In case you are using some kind of ICollectionView - you do not need that property on your ViewModel.
<HierarchicalDataTemplate x:Key="LogFolderExplorer"
DataType="{x:Type TestAutomationClient:TestArtifact}"
ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal" MinWidth="200">
<CheckBox Focusable="False"
IsChecked="{Binding IsChecked}"
VerticalAlignment="Center">
<CheckBox.Style>
<Style TargetType="CheckBox">
<Setter Property="Visibility" Value="Visible" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsEmpty}" Value="True">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
<TextBlock Text="{Binding Name}" FontSize="14"/>
</StackPanel>
</HierarchicalDataTemplate>
You need a DataTrigger with a Converter. The Trigger should look like below,
<HierarchicalDataTemplate ItemsSource="{Binding SubItems}">
<StackPanel Orientation="Horizontal">
<CheckBox x:Name="chk" Visibility="Collapsed"/>
<TextBlock Text="{Binding Name}"
VerticalAlignment="Center"
Margin="3 0" />
</StackPanel>
<HierarchicalDataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Converter={StaticResource TreeLevelFinder}}"
Value="True">
<Setter Property="Visibility"
TargetName="checkBox"
Value="Visible" />
</DataTrigger>
</HierarchicalDataTemplate.Triggers>
</HierarchicalDataTemplate>
The whole TreeViewItem is being send to the Converter. The Converter will look for a Visual Parent. If it finds a Parent as TreeView, it is the first level. If it finds TreeViewItem, then it is sub levels.
public class TreeLevelFinder : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is FrameworkElement)
{
var element = value as FrameworkElement;
var treeItem = element.TemplatedParent as TreeViewItem;
var parent = treeItem.FindAncestor<ItemsControl>();
if (parent is TreeView)
{
return true;
}
}
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
The extension method FindAncestor<T> is available in this link.
I'm trying to create a page indicator using ItemsControl. The idea is to bind to the tabs of a TabControl and display a circle for each tab, with the color determined by a trigger that checks whether the tab is selected or not:
<ItemsControl ItemsSource="{Binding Path=Items, ElementName=ATabControl}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type TabItem}">
<Ellipse x:Name="PageIndicator" Width="6" Height="6" Margin="2,0" />
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=IsSelected}" Value="False">
<Setter TargetName="PageIndicator" Property="Fill" Value="White" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=IsSelected}" Value="True">
<Setter TargetName="PageIndicator" Property="Fill" Value="Blue" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
It compiles without error but instead of the circles, I get the names of the tabs listed. Practically, it ignores the ItemTemplate/DataTemplate completely. Actually, if I remove the latter, the display remains the same all right.
There is always an issue while binding UI elements in two different containers. as one UI element can have only 1 parent so the last parent will have the actual element and hence the same will be removed from the former parent containers.
in the issue you've mentioned you attempted to bind the UI element TabItems to the items control which effectively pulled the original elements from the tab control and placed them as child of the items control.
in order to solve this issue I propose a solution to wrap such UI elements in a class and wire the properties needed.
I attempted a solution using converter
xaml
<StackPanel xmlns:l="clr-namespace:CSharpWPF">
<StackPanel.Resources>
<l:TabItemsConverter x:Key="TabItemsConverter" />
</StackPanel.Resources>
<TabControl x:Name="ATabControl">
<TabItem Header="item 1" />
<TabItem Header="item 2" />
<TabItem Header="item 3" />
</TabControl>
<ItemsControl ItemsSource="{Binding Path=Items, ElementName=ATabControl,Converter={StaticResource TabItemsConverter}}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Center" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Ellipse x:Name="PageIndicator"
Width="6"
Height="6"
Margin="2,0"
Fill="Gray" />
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=IsSelected}"
Value="False">
<Setter TargetName="PageIndicator"
Property="Fill"
Value="Gray" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=IsSelected}"
Value="True">
<Setter TargetName="PageIndicator"
Property="Fill"
Value="Blue" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
converter class
namespace CSharpWPF
{
public class TabItemsConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
List<TabItemWrapper> result = new List<TabItemWrapper>();
foreach (TabItem item in (ItemCollection)value)
{
result.Add(new TabItemWrapper(item));
}
return result;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
class TabItemWrapper : DependencyObject
{
public bool IsSelected
{
get { return (bool)GetValue(IsSelectedProperty); }
set { SetValue(IsSelectedProperty, value); }
}
// Using a DependencyProperty as the backing store for IsSelected. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsSelectedProperty =
DependencyProperty.Register("IsSelected", typeof(bool), typeof(TabItemWrapper), new PropertyMetadata(false));
public TabItemWrapper(TabItem source)
{
Binding b = new Binding("IsSelected");
b.Source = this;
b.Mode = BindingMode.TwoWay;
source.SetBinding(TabItem.IsSelectedProperty, b);
}
}
}
}
in this converter I binded the tab item's IsSelected to the property of a wrapper class and used it to bind in the view
Note: this converter work for static tab items only, if you intend to add or remove the tab items during runtime then perhaps you may need to handle CollectionChanged events to keep the result in sync.
result
I have a few images defined in my resource section of my UI :
<Window.Resources>
<!-- Converters -->
<loc:UserStatusToIconConverter x:Key="UserStatusToIconConverter" />
<!-- Images -->
<BitmapImage x:Key="ConnectIcon" UriSource="/WPFClient;component/Images/connect.png" />
<BitmapImage x:Key="ActiveIcon" UriSource="/WPFClient;component/Images/active.png" />
<BitmapImage x:Key="IdleIcon" UriSource="/WPFClient;component/Images/idle.png" />
<BitmapImage x:Key="AwayIcon" UriSource="/WPFClient;component/Images/away.png" />
<BitmapImage x:Key="UnknownIcon" UriSource="/WPFClient;component/Images/unknown.png" />
...
I would like to select one of these for a binding in my converter, I assume this would be more efficient than creating a new image each time (500 times) from the converter.
public class UserStatusToIconConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string userStatus = value.ToString();
string iconName = ...;
switch (userStatus)
{
case "Active":
// select ActiveIcon;
break;
case "Idle":
// select IdleIcon;
break;
case "Away":
...
break;
}
return iconName;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Here is where I use it :
<ListBox ItemsSource="{Binding Users}">
<ListBox.ItemTemplate>
<DataTemplate>
<DockPanel>
<Image Source="{Binding Status, Converter={StaticResource UserStatusToIconConverter}}" Height="16" Width="16" />
<TextBlock Text="{Binding Nick}" />
</DockPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I think you're better off using DataTemplate.Triggers in this case rather than a Converter:
<DataTemplate>
<DockPanel>
<Image x:Name="Img" Height="16" Width="16" />
<TextBlock Text="{Binding Nick}" />
</DockPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Status}" Value="Active">
<Setter TargetName="Img" Property="Source" Value="{StaticResource ActiveIcon}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Status}" Value="Idle">
<Setter TargetName="Img" Property="Source" Value="{StaticResource IdleIcon}"/>
</DataTrigger>
<!-- And So on... -->
</DataTemplate.Triggers>
</DataTemplate>
You may just do the following in your Convert method:
return Application.Current.MainWindow.FindResource(iconName);