Skip to content
This repository was archived by the owner on Jun 21, 2023. It is now read-only.

Commit a75f2e2

Browse files
committed
Add a xaml behavior and converter to support [None] items on top of lists
For lists where we want to support having no selection, it's handy to have an "empty" item on top of the list for the user to select. The behavior added here will add such an item to the top of a list if the selected property has a value set in it. If the selected property is null, the behavior removes the "empty" item. The behavior requires that the `ItemsSource` binding of the ItemsControl it's attached to be a MultiBinding, where the first entry is the bound property that contains the "empty" item to show. The `StickieListItemConverter` makes sure that the ItemsSource gets set with the second entry of the MultiBinding.
1 parent a65a587 commit a75f2e2

File tree

3 files changed

+88
-0
lines changed

3 files changed

+88
-0
lines changed
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
using System.Windows;
2+
using System.Windows.Controls;
3+
using System.Windows.Data;
4+
using System.Windows.Interactivity;
5+
6+
namespace GitHub.UI
7+
{
8+
public class AddEmptyItemToList : Behavior<ListBox>
9+
{
10+
object previousSelection = null;
11+
dynamic defaultValue;
12+
protected override void OnAttached()
13+
{
14+
base.OnAttached();
15+
16+
AssociatedObject.SelectionChanged += (s, e) =>
17+
{
18+
if (e.AddedItems.Count > 0)
19+
{
20+
if (previousSelection == null)
21+
{
22+
previousSelection = AssociatedObject.SelectedItem;
23+
if (defaultValue == null)
24+
{
25+
var binding = AssociatedObject.GetBindingExpression(ListBox.ItemsSourceProperty);
26+
if (binding == null)
27+
{
28+
var m = BindingOperations.GetMultiBindingExpression(AssociatedObject, ListBox.ItemsSourceProperty);
29+
binding = (BindingExpression)m.BindingExpressions[0];
30+
}
31+
defaultValue = PropertyPathHelper.GetValue(binding.ResolvedSource, binding.ResolvedSourcePropertyName);
32+
}
33+
dynamic items = AssociatedObject.ItemsSource;
34+
items.Insert(0, defaultValue);
35+
}
36+
else if (AssociatedObject.SelectedIndex == 0)
37+
{
38+
dynamic items = AssociatedObject.ItemsSource;
39+
items.RemoveAt(0);
40+
}
41+
}
42+
if (e.RemovedItems.Count > 0)
43+
{
44+
if (e.RemovedItems[0] == defaultValue)
45+
{
46+
previousSelection = null;
47+
AssociatedObject.SelectedItem = null;
48+
}
49+
}
50+
};
51+
}
52+
53+
static class PropertyPathHelper
54+
{
55+
public static object GetValue(object obj, string propertyPath)
56+
{
57+
var binding = new Binding(propertyPath);
58+
binding.Mode = BindingMode.OneTime;
59+
binding.Source = obj;
60+
BindingOperations.SetBinding(dummy, Dummy.ValueProperty, binding);
61+
return dummy.GetValue(Dummy.ValueProperty);
62+
}
63+
64+
static readonly Dummy dummy = new Dummy();
65+
66+
class Dummy : DependencyObject
67+
{
68+
public static readonly DependencyProperty ValueProperty =
69+
DependencyProperty.Register("Value", typeof(object), typeof(Dummy), new UIPropertyMetadata(null));
70+
}
71+
}
72+
}
73+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using System;
2+
using System.Globalization;
3+
4+
namespace GitHub.UI
5+
{
6+
public class StickieListItemConverter : MultiValueConverterMarkupExtension<StickieListItemConverter>
7+
{
8+
public override object Convert(object[] value, Type targetType, object parameter, CultureInfo culture)
9+
{
10+
return value[1];
11+
}
12+
}
13+
}

src/GitHub.UI/GitHub.UI.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
<Reference Include="WindowsBase" />
7878
</ItemGroup>
7979
<ItemGroup>
80+
<Compile Include="Behaviours\AddEmptyItemToList.cs" />
8081
<Compile Include="Behaviours\ClosePopupAction.cs" />
8182
<Compile Include="Behaviours\OpenPopupAction.cs" />
8283
<Compile Include="Controls\DropDownActionLinkBehaviour.cs" />
@@ -86,6 +87,7 @@
8687
<DependentUpon>OcticonPaths.resx</DependentUpon>
8788
</Compile>
8889
<Compile Include="Converters\DefaultValueConverter.cs" />
90+
<Compile Include="Converters\StickieListItemConverter.cs" />
8991
<Compile Include="Resources.Designer.cs">
9092
<AutoGen>True</AutoGen>
9193
<DesignTime>True</DesignTime>

0 commit comments

Comments
 (0)