I want to use linq grouping as itemssource for checkbox listview
It my data model:
public class GroupItem
{
public string name GroupName { get; set; }
public string boolean GroupItemFlag { get; set; }
}
It view model (data context):
...
IEnumerable<GroupItem> _groupItems;
public IEnumerable<IGrouping<string,GroupItem>> Groups
{
get { return _groupItems.GroupBy(__item=>__item.GroupName); }
}
...
it view:
...
<ListView ItemsSource={Binding Groups}>
<ListView.ItemTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Converter={StaticResource groupingToBooleanConverter}, Mode=TwoWay}"/>
<TextBlock Text="{Binding Key}"/>
</StackPanel>
</ListView.ItemTemplate>
</ListView>
...
groupingToBooleanConverter code:
public class GroupingToBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var group = value as IGrouping<string,GroupItem>;
return group.Any(__item => __item.GroupItemFlag);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
// problem here
// how to set for all group items GroupItemFlag=(bool)value;
}
}
I don't want create one more class "Group" for this, so i use IGrouping and converter.
May be I chose the wrong way?
You can achieve it even without converter:
<CheckBox IsChecked="{Binding Path=GroupItemFlag, Mode=OneWay}"
Command="{Binding DataContext.ToogleGroupCommand, ElementName=LayoutRoot}"
CommandParameter="{Binding}"/>
void ToogleGroupCommand_Execute(IGrouping<string, GroupItem> group){
bool newValue = !group.First().GroupItemFlag;
foreach(var item in group) item.GroupItemFlag = newValue;
}
if you are not using MVVM, then just handle Checked and Unchecked events.
However, if you are using MVVM, I highly recommend you to create Group class as ViVi suggests:
public MainViewModel()
{
Groups = _groupItems.GroupBy(i => i.GroupName).Select(i => new GroupViewModel(i.Key, i);
}
public Groups[] Groups {get;}
public class GroupViewModel
{
public GroupViewModel(string name, IEnumerable<GroupItem> items)
{
Items = items;
}
public string Name { get; }
public IEnumerable<GroupItem> Items { get; }
public bool? IsChecked
{
get
{
if (Items.All(i => i.GroupItemFlag)) return true;
if (Items.Any(i => i.GroupItemFlag)) return null;
return false;
}
set
{
foreach (var item in Items)
{
item.GroupItemFlag = value.GetValueOrDefault();
}
}
}
}
Related
I have a collection of items that I have bound to a WPF DataGrid. One of the columns needs to allow the user to select from an enumeration. I have also created a separate class to hold user-friendly display strings for each of the enumeration values.
These are the types:
public enum UnitEnum { Metres, Hours, SingleValue };
public class UnitTuple
{
public UnitEnum Unit { get; set; }
public string DisplayName { get; set; }
}
public class CostItem
{
public string Name { get; set; }
public UnitEnum UnitID { get; set; }
}
And this is my view-model:
public static List<UnitTuple> AllUnitTuples = new List<UnitTuple>()
{
new UnitTuple { Unit = UnitEnum.Metres, DisplayName = "Metres" },
new UnitTuple { Unit = UnitEnum.Hours, DisplayName = "Hours" },
new UnitTuple { Unit = UnitEnum.SingleValue, DisplayName = "Single Value" }
};
public List<CostItem> CostItems => new List<CostItem>()
{
new CostItem() { Name = "Design", UnitID = UnitEnum.SingleValue },
new CostItem() { Name = "Manufacturing", UnitID = UnitEnum.Hours },
new CostItem() { Name = "Materials", UnitID = UnitEnum.Metres }
};
And this is the start of the XAML I am using:
<DataGrid x:Name="DataGridCostItems"
ItemsSource="{Binding CostItems}"
AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Cost Item Name" Binding="{Binding Name}"/>
<DataGridComboBoxColumn Header="Units"/>
</DataGrid.Columns>
</DataGrid>
Can anyone help me out with the remaining data-binding expressions (e.g. ItemsSource, SelectedValueBinding)?
I have been trying to follow the two examples shown below but am having trouble mentally mapping these examples to my code.
Codeless two-way data binding
How to bind an enumerator
Following on from the answer by Aakanksha, I tried to get an ObjectDataProvider working but couldn't. The problem I was encountering - which is that the DataGridComboBoxColumn apparently does not have the data context of the view-model - is explained here: Cannot find governing FrameworkElement.
This same article suggested the use of CollectionViewSource instead. Attempting this solution has lead to the code below, which almost works: the combo-box column is correctly populated with the user-friendly display strings and when I select one to edit, I get a drop-down containing the user-friendly display strings for the enum. All good.
The only part I now cannot get to work for the life of me is getting the newly selected value in the combo-box to persist back to the data-bound CostItem.
Can anyone suggest how to fix this last piece of the puzzle?
public enum CostUnit
{
SingleValue,
Hours,
Metres
}
public class CostUnitTuple
{
public EstimateCostUnitTuple( EstimateCostUnit enumValue, string displayString )
{
EnumValue = enumValue;
DisplayString = displayString;
}
public CostUnit EnumValue { get; private set; }
public string DisplayString { get; private set; };
}
public static class CostUnitAllTuples
{
public static string GetDisplayStringFromEnum( CostUnit enumValue ) { ... }
public static CostUnit GetEnumFromDisplayString( string displayString ) { ... }
public static CostUnitTuple[] GetValues => new CostUnitTuple[]
{
new CostUnitTuple( CostUnit.Hours, "Hours" ),
new CostUnitTuple( CostUnit.Metres, "Metres" ),
new CostUnitTuple( CostUnit.SingleValue, "Single Value" )
};
}
public class CostItem
{
public string Name { get; set; } = "";
public CostUnit Units
{
get => _units;
set
{
_units = value;
UnitDisplayString = CostUnitAllTuples.GetDisplayStringFromEnum( _units );
}
}
public string UnitDisplayString { get; private set; } = "";
private CostUnit _units;
}
public class UnitsOfMeasureConverter : IValueConverter
{
public object Convert( object value, Type targetType, object parameter, CultureInfo culture ) =>
CostUnitAllTuples.GetDisplayStringFromEnum( (CostUnit)value );
public object ConvertBack( object value, Type targetType, object parameter, CultureInfo culture ) =>
((EstimateCostUnitTuple)value).EnumValue;
}
public class ViewModel
{
public List<CostItem> CostItems { get; set; }
public AllCostUnits => CostUnitAllTuples.GetValues;
}
<Control.Resources>
<ResourceDictionary>
<u:UnitsOfMeasureConverter x:Key="UnitsOfMeasureConverterKey"/>
<CollectionViewSource x:Key="UnitsCollectionViewSourceKey" Source="{Binding AllCostUnits}"/>
</ResourceDictionary>
</Control.Resources>
<DataGrid
ItemsSource="{Binding CostItems}"
SelectionMode="Single">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}"/>
<DataGridComboBoxColumn Header="Units"
TextBinding="{Binding UnitDisplayString,Mode=OneWay}"
SelectedItemBinding="{Binding Units,Converter={StaticResource UnitsOfMeasureConverterKey}}"
DisplayMemberPath="DisplayString"
ItemsSource="{Binding Source={StaticResource UnitsCollectionViewSourceKey}}"/>
</DataGrid.Columns>
</DataGrid>
Your can use a converter to display enum in your datagrid.
public class EnumConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((Enum)value).ToString()
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
And in your XAML, something like this:
Binding="{Binding UnitID, Converter="{StaticResource ResourceKey=EnumConverter}}"
Declare your "EnumConverter" in the resources section of your xaml file.
One of the ways is:
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{x:Static local:YourViewModelClassName.AllUnitTuples}" SelectedValuePath="Unit" SelectedValue="{Binding UnitID, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
To bind enum to your DataGridComboBox
Add a ObjectDataProvider in the resources section.
<Window.Resources>
<ObjectDataProvider x:Key="myEnumData" MethodName="GetValues" ObjectType="{x:Type sys:Enum}">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="local:UnitEnum" />
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</Window.Resources>
And in the DataGridComboBoxColumn bind the ItemsSource like this,
<DataGridComboBoxColumn Header="MyHeader" DisplayMemberPath="EnumValue" ItemsSource="{Binding Source={StaticResource myEnumData}}" SelectedItemBinding="{Binding UnitID}"/>
I would like to display "Yes" or "No" for whenever a Boolean type data received(it can receive different types of data) for generating a column in RadGridView instead of a checkbox. I would like to implement this changes in xaml. Columns are generating dynamically. This is how it's created now:
<telerik:RadGridView x:Name="Data" Grid.Row="3" Margin="5" AutoGenerateColumns="False" CanUserSortColumns="True" IsFilteringAllowed="True"
grid:RadGridViewColumnsBinding.ColumnsCollection="{Binding Path=ColumnsData}"
IsReadOnly="False" CanUserResizeColumns="True"/>
I am new in Silverlight coding. Will really appreciate if someone can help.
You should check out Telerik's ConditionalDataTemplateSelector they have in this demo, and read about IValueConverter if you haven't already.
Depending on what you are trying to do with all your columns, the ConditionalDataTemplateSelector might be overkill, but you can use it to create a rule system for what DataTemplate to use for a given cell based on a custom rule system.
<Grid.Resources>
...
<DataTemplate x:Key="CellDisplayTextBox">
<TextBlock Text="{Binding Value, Converter={StaticResource BooleanToYesNoConverter}}" />
</DataTemplate>
<selector:ConditionalDataTemplateSelector x:Key="displaySelector" ConditionConverter="{StaticResource someConverter}">
<selector:ConditionalDataTemplateSelector.Rules>
<selector:ConditionalDataTemplateRule DataTemplate="{StaticResource CellDisplayTextBox}">
<selector:ConditionalDataTemplateRule.Value>
<sys:Int32>1</sys:Int32> <!--You need to figure out what value and type to use here -->
</selector:ConditionalDataTemplateRule.Value>
</selector:ConditionalDataTemplateRule>
...
</selector:ConditionalDataTemplateSelector.Rules>
</Grid.Resources>
...
<telerikGridView:RadGridView>
<telerik:RadGridView.Columns>
<telerik:GridViewDataColumn CellTemplateSelector="{StaticResource displaySelector}" CellEditTemplateSelector="{StaticResource editSelector}" />
</telerik:RadGridView.Columns>
</telerikGridView:RadGridView>
The IValueConverter will let you bind a bool value, but display a string value. For a BooleanToYesNoConverter you could do something like:
public class BooleanToYesNoConverter: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool? bValue = value as bool?;
if (bValue.HasValue)
return bValue.Value ? "Yes" : "No";
else
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
string sValue = value as string;
return sValue == "Yes";
}
}
The ConditionalDataTemplateSelector code from the demo:
public class ConditionalDataTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
object conditionValue = this.ConditionConverter.Convert(item, null, null, null);
foreach (ConditionalDataTemplateRule rule in this.Rules)
{
if (Equals(rule.Value, conditionValue))
{
return rule.DataTemplate;
}
}
return base.SelectTemplate(item, container);
}
List<ConditionalDataTemplateRule> _Rules;
public List<ConditionalDataTemplateRule> Rules
{
get
{
if (this._Rules == null)
{
this._Rules = new List<ConditionalDataTemplateRule>();
}
return this._Rules;
}
}
IValueConverter _ConditionConverter;
public IValueConverter ConditionConverter
{
get
{
return this._ConditionConverter;
}
set
{
this._ConditionConverter = value;
}
}
}
public class ConditionalDataTemplateRule
{
object _Value;
public object Value
{
get
{
return this._Value;
}
set
{
this._Value = value;
}
}
DataTemplate _DataTemplate;
public DataTemplate DataTemplate
{
get
{
return this._DataTemplate;
}
set
{
this._DataTemplate = value;
}
}
}
I have a list control and each item contains two images and text. On the click on each item I want to hide or show selected image on selected list item.
Here is XAML code snippet:
<ListBox x:Name="list" SelectionChanged="list_SelectionChanged" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}"/>
<Image Source="{Binding ImagePath}" Stretch="None"/>
<Image Source="{Binding ImagePath}" Stretch="None"
Visibility="{Binding ImageVisibility,
Converter={StaticResource boolVisibilityConverter}}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
C# code:
dataSource = new ObservableCollection<ImageData>()
{
new ImageData(){Name = "User1:", ImagePath="/Images/user1.png", ImageVisibility = false},
new ImageData(){Name = "User1:", ImagePath="/Images/user1.png", ImageVisibility = true},
new ImageData(){Name = "User1:", ImagePath="/Images/user1.png", ImageVisibility = true},
new ImageData(){Name = "User2:", ImagePath="/Images/user2.png", ImageVisibility = true}
};
List Selection Changed Event:
private void list_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
((ImageData)(((object[])(e.AddedItems))[0])).ImageVisibility = false;
list.UpdateLayout();
}
ImageData class:
public class ImageData
{
public string ImagePath { get; set; }
public string Name { get; set; }
public bool ImageVisibility { get; set; }
}
Image Visibility Converter:
public class BooleanToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool flag = false;
if (value is bool)
{
flag = (bool)value;
}
else if (value is bool?)
{
bool? nullable = (bool?)value;
flag = nullable.HasValue ? nullable.Value : false;
}
return (flag ? Visibility.Visible : Visibility.Collapsed);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((value is Visibility) && (((Visibility)value) == Visibility.Visible));
}
}
Please help me to accomplish such functionality.
You need to use INotifyPropertyChanged interface http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged.aspx
For example so:
public class ImageData : INotifyPropertyChanged
{
private bool _imageVisibility;
public string ImagePath { get; set; }
public string Name { get; set; }
public bool ImageVisibility
{
get
{
return _imageVisibility;
}
set
{
_imageVisibility = value;
OnPropertyChanged("ImageVisibility");
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Full changes according to your task you can see here (dropbox)
Is there a way to bind to the ItemIndex from within the ItemTemplate of an ItemsControl?
For example:
<ItemsControl ItemsSource="{Binding Path=ItemList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=ThisItemsIndex}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
If you're not using any type of alternating row styles you might be able to hijack the AlternationIndex for this. Set AlternationCount on your ItemsControl to something greater than the max possible count of your items and then use
Text="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=(ItemsControl.AlternationIndex)}"
Edit: As bradgonesurfing pointed out in comments, this is not recommended if you're using virtualization, as it will only index the items that are generated and not the entire list.
Here is a method I used to add a bindable index on a collection item. I basically wrap my item in a container that has an index, and have a custom ObservableCollection that accepts the wrapper.
Note that MoveItem is not overridden, but would have to be for a complete implementation.
public class IndexedItemContainerCollection<T> : ObservableCollection<IndexedItemContainer<T>>
{
public IndexedItemContainerCollection()
{
}
public IndexedItemContainerCollection(IEnumerable<IndexedItemContainer<T>> collection)
: base(collection)
{
var index = 0;
foreach (var item in this)
{
item.Index = index;
}
}
protected override void InsertItem(int index, IndexedItemContainer<T> item)
{
item.Index = index;
base.InsertItem(index, item);
foreach (var indexedItem in this.Where(x=>x.Index > index))
{
indexedItem.Index++;
}
}
protected override void RemoveItem(int index)
{
base.RemoveItem(index);
foreach (var indexedItem in this.Where(x => x.Index > index))
{
indexedItem.Index--;
}
}
}
public class IndexedItemContainer<T>
{
public int Index { get; set; }
public T Item { get; set; }
}
I then extend my wrapper class to get a bindable property that I have control over how the index is displayed:
public class NamedIndexedItemContainer<T> : IndexedItemContainer<T>
{
public string Name
{
get { return string.Format("Item #{0}", Index + 1); }
}
}
Sample Usage
XAML:
<ComboBox ItemsSource="{Binding ItemList}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Code:
private IndexedItemContainerCollection<MyItem> _itemList;
public IndexedItemContainerCollection<MyItem> ItemList
{
get { return _itemList; }
set { _itemList= value; OnPropertyChanged(); }
}
ItemList = new IndexedItemContainerCollection<MyItem>();
var newItem = new NamedIndexedItemContainer<MyItem>() { Item = new MyItem() { ... } };
ItemList.Add(newItem);
Of course, any binding with the actual MyItem instance would have to go through the IndexedItemContainer's Item property.
For the record, there is another way to accomplish this: using custom Converter. A little bit more complicated, but you do not have to worry about AlternationCount/Index.
public sealed class ArrayWrapperConverter : IValueConverter
{
private static readonly Type ArrayWrappingHelperType = typeof(ArrayWrappingHelper<>);
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null)
{
return null;
}
Type valueType = value.GetType();
if (!valueType.IsArray)
{
return DependencyProperty.UnsetValue;
}
Type elementType = valueType.GetElementType();
Type specificType = ArrayWrappingHelperType.MakeGenericType(elementType);
IEnumerable wrappingHelper = (IEnumerable) Activator.CreateInstance(specificType, value);
return wrappingHelper;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
internal class ArrayWrappingHelper<TValue> : IEnumerable
{
private readonly TValue[] _array;
public ArrayWrappingHelper(object array)
{
_array = (TValue[]) array;
}
public IEnumerator GetEnumerator()
{
return _array.Select((item, index) => new ArrayItemWrapper<TValue>(_array, index)).GetEnumerator();
}
}
public class ArrayItemWrapper<TValue>
{
private readonly TValue[] _array;
private readonly int _index;
public int Index
{
get { return _index; }
}
public TValue Value
{
get { return _array[_index]; }
set { _array[_index] = value; }
}
public ArrayItemWrapper(TValue[] array, int index)
{
_array = array;
_index = index;
}
}
Sample usage:
<Window x:Class="WpfArrayBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:c="clr-namespace:WpfArrayBinding.Converters"
xmlns:s="clr-namespace:System;assembly=mscorlib"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<ResourceDictionary>
<c:ArrayWrapperConverter x:Key="ArrayWrapperConverter" />
<x:Array Type="{x:Type s:String}" x:Key="MyArray">
<s:String>Foo</s:String>
<s:String>Bar</s:String>
<s:String>Baz</s:String>
</x:Array>
</ResourceDictionary>
</Window.Resources>
<ItemsControl ItemsSource="{Binding Source={StaticResource MyArray}, Converter={StaticResource ArrayWrapperConverter}}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Index}" />
<TextBox Text="{Binding Value}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Window>
Here is what happens:
I have a listbox with items. Listbox has focus. Some item (say, 5th) is selected (has a blue background), but has no 'border'.
When I press 'Down' key, the focus moves from ListBox to the first ListBoxItem.
(What I want is to make 6th item selected, regardless of the 'border')
When I navigate using 'Tab', the Listbox never receives the focus again.
But when the collection is emptied and filled again, ListBox itself gets focus, pressing 'Down' moves the focus to the item.
How to prevent ListBox from gaining focus?
P.S.
listBox1.SelectedItem is my own class, I don't know how to make ListBoxItem out of it to .Focus() it.
EDIT: the code
Xaml:
<UserControl.Resources>
<me:BooleanToVisibilityConverter x:Key="visibilityConverter"/>
<me:BooleanToItalicsConverter x:Key="italicsConverter"/>
</UserControl.Resources>
<ListBox x:Name="lbItems">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<ProgressBar HorizontalAlignment="Stretch"
VerticalAlignment="Bottom"
Visibility="{Binding Path=ShowProgress, Converter={StaticResource visibilityConverter}}"
Maximum="1"
Margin="4,0,0,0"
Value="{Binding Progress}"
/>
<TextBlock Text="{Binding Path=VisualName}"
FontStyle="{Binding Path=IsFinished, Converter={StaticResource italicsConverter}}"
Margin="4"
/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
<me:OuterItem Name="Regular Folder" IsFinished="True" Exists="True" IsFolder="True"/>
<me:OuterItem Name="Regular Item" IsFinished="True" Exists="True"/>
<me:OuterItem Name="Yet to be created" IsFinished="False" Exists="False"/>
<me:OuterItem Name="Just created" IsFinished="False" Exists="True"/>
<me:OuterItem Name="In progress" IsFinished="False" Exists="True" Progress="0.7"/>
</ListBox>
where OuterItem is:
public class OuterItem : IOuterItem
{
public Guid Id { get; set; }
public string Name { get; set; }
public bool IsFolder { get; set; }
public bool IsFinished { get; set; }
public bool Exists { get; set; }
public double Progress { get; set; }
/// Code below is of lesser importance, but anyway
///
#region Visualization helper properties
public bool ShowProgress
{
get
{
return !IsFinished && Exists;
}
}
public string VisualName
{
get
{
return IsFolder ? "[ " + Name + " ]" : Name;
}
}
#endregion
public override string ToString()
{
if (IsFinished)
return Name;
if (!Exists)
return " ??? " + Name;
return Progress.ToString("0.000 ") + Name;
}
public static OuterItem Get(IOuterItem item)
{
return new OuterItem()
{
Id = item.Id,
Name = item.Name,
IsFolder = item.IsFolder,
IsFinished = item.IsFinished,
Exists = item.Exists,
Progress = item.Progress
};
}
}
Сonverters are:
/// Are of lesser importance too (for understanding), but will be useful if you copy-paste to get it working
public class BooleanToItalicsConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
bool normal = (bool)value;
return normal ? FontStyles.Normal : FontStyles.Italic;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class BooleanToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
bool exists = (bool)value;
return exists ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
But most important, is that UserControl.Loaded() has:
lbItems.Items.Clear();
lbItems.ItemsSource = fsItems;
where fsItems is ObservableCollection<OuterItem>.
The usability problem I describe takes place when I Clear() that collection (fsItems) and fill with new items.
Please provide your code. Usually the cause of this problem lies in ContentPresenters and KeyboardNavigation.IsTabStop property. But sometimes it's not. So the code would help.
The answer to your question may depend on the way your listbox is getting focus. Here is the solution if you are using an access key (ex: alt+c). You have to implement your own listbox control and override the OnAccessKey method. If this is not your scenario, then I would suggest looking into the OnIsKeyboardFocusWithinChanged method. Try using the same approach I did in the code below.
protected override void OnAccessKey(System.Windows.Input.AccessKeyEventArgs e)
{
if (SelectedIndex >= 0)
{
UIElement element = ItemContainerGenerator.ContainerFromIndex(SelectedIndex) as UIElement;
if (element != null)
{
element.Focus();
}
}
}