Databinding to entries in a dictionary with enums as keys - c#

I'm attempting to databind to an entry in a dictionary where the key is the enum. I've consulted this question, but the answer doesn't work for me. Here are the non-boilerplate parts of my code:
SomePage.xaml:
<!-- Here I try all the ways I can think of. None of them produce any text -->
<TextBlock Text="{Binding Data[0]}" />
<TextBlock Text="{Binding Data[EnumValueA]}" />
<TextBlock Text="{Binding Data[SomeEnum.EnumValueA]}" />
<TextBlock Text="{Binding Data[(local:SomeEnum)EnumValueA]}" />
<TextBlock Text="{Binding Data[(local:SomeEnum)SomeEnum.EnumValueA]}" />
SomePage.xaml.cs:
public SomePage() {
DataContext = new SomeVM();
InitializeComponent();
}
SomeVM.cs:
public enum SomeEnum {
EnumValueA, EnumValueB
}
public class SomeVM {
public Dictionary<SomeEnum, int> Data { get; private set; }
public SomeVM() {
Data = new Dictionary<SomeEnum, int> {
{SomeEnum.EnumValueA, 1337}
};
}
}
Why does this databinding not work?

It is not possible. See http://msdn.microsoft.com/en-us/library/cc645024(v=vs.95).aspx#indexdata
Indexer
Indexers can be used for accessing properties in a path and obtaining
items from a list, but with some notable restrictions:
List item
Numeric integer indexers are supported.
Beginning in Silverlight 4, string indexers are supported.
Only one-dimensional array indexing is supported.
The type being indexed must implement or inherit IList. (List is
accepted, because it implements IList. However IList is not
accepted.)
Numeric integer indexes are specified by declaring the zero-based
index within bracket ([]) characters after the property name; for
example, Employees[2].
Property path evaluation first attempts to use integer indexing for a
collection. If that indexing is not valid for the collection, then the
information within [] is processed as a string. String indexing from a
property path generally expects a collection / business object that is
a dictionary with string keys. String indexing supports binding to
dynamic data objects where the CLR structure of the data source can
change, but the string keys represent a data contract that can still
be bound to by a client UI.
Validation uses indexers to access items of an attached property's
collection as part of its property path usage. The validation
structure for the application can declare UI states within templates
that are used only when validation errors are raised, and can then
reference the active error objects in that context. For example, the
following is a property path for a binding that accesses the first
item in a Validation.Errors collection; the context of the property
path is modified by RelativeSource so that the errors are checked only
at run time on the applied template:
<TextBlock Text="{Binding RelativeSource={RelativeSource
TemplatedParent}, Path=(Validation.Errors)[0].Exception.Message }">

Related

Getting index of an item in an ObservableCollection inside the item

I'd like to be able to display an index value from within a DataTemplate, but I don't want the data to be persisted or backed by the model or viewmodel. In other words, if the order of the items in the OC changes, I don't want to have to recalculate the indexes. The value should be intrinsically tied to the underlying index in the OC. It is okay if the index is 0-based (in fact, I'd expect it).
One method that others have used is the AlternationIndex AP, but this has its own pitfalls for certain situations.
One last thought: I can't help but think that a converter is going to be helpful in a final solution.
I would use a converter to do this.
The trick is giving it the source collection, either on the ConverterParameter or a Dependency Property. At that point, conversion is as simple as using IndexOf.
Here's a sample converter that does this:
public class ItemToIndexConverter : IValueConverter
{
public object Convert(...)
{
CollectionViewSource itemSource = parameter as CollectionViewSource;
IEnumerable<object> items = itemSource.Source as IEnumerable<object>;
return items.IndexOf(value as object);
}
public object ConvertBack(...)
{
return Binding.DoNothing;
}
}
You can make the implementation strongly typed, return a formatted string as a number, etc. The basic pattern will be as above though.
This implementation uses the parameter approach, as making a DP is more messy in my view. Because you can't bind ConverterParameter, I have it set to a static resource that is bound to the collection:
<CollectionViewSource x:Key="collectionSource" Source="{Binding Path=MyCollection}" />
...
<TextBlock Text="{Binding Converter={StaticResource ResourceKey=ItemToIndexConverter},
ConverterParameter={StaticResource ResourceKey=collectionSource}}"/>

How to set fallback value for x:Static

Subj.
Can do like this to avoid having blurish view in design:
<Window.Effect>
<BlurEffect Radius="{Binding Blur, FallbackValue=0}"/>
</Window.Effect>
But what about
<TextBlock ext="{x:Static local:App.Version}"/>
at design time auto-property App.Version is null. I can make it normal property and assign private field default value:
private static string _version = "Version1.0.0.0";
public static string Version { get { return _version; } }
Still there can be a situation when I want non-default value to be displayed. To example,
"Test long version string to be visible in designer only"
And yes, I understand, what Binding and Static are different in someway, yet, is there a way to achieve what I want? I also though to pass App.Version into ViewModel and bind View to it via Binding, but that's even worse (effort-wise), though more mvvm-conceptish.
How about:
<TextBlock Text="{Binding Source={x:Static local:App.Version}, TargetNullValue='In designer'}" />
Note that you have to use TargetNullValue as FallbackValue is used when Binding cannot get value, which should not be the case for static property.

Data Binding Combo Box in C# WPF

I'm having an issue with C# using WPF.
Just being brief here.
The following code below collects names via Entity Framework into a list.
This is in my MainWindow.xaml.cs file.
public ObservableCollection<string> FruitInfo
{
get
{
using (var context = new Fruit())
{
ObservableCollection<string> fruits= new ObservableCollection<string>();
foreach (var item in context.Fruits.OrderBy(s => s.FruitName))
{
fruits.Add(item.FruitName);
}
return fruits;
}
}
}
In my MainWindow.xaml file, I have the following:
<GroupBox Grid.Row="0" Grid.Column="0" Margin="5" Header="Fruit Info" >
<ComboBox Margin="5" SelectedItem="{Binding FruitInfo}"/>
</GroupBox>
When running my project, I see that the Combo Box does not populate the fruits.
Any ideas why I'm not seeing this?
All thoughts appreciated
You should bind the ItemsSource of the ComboBox to your collection, and the SelectedItem to another string that will represent the user's selection.
First:
<GroupBox Grid.Row="0" Grid.Column="0" Margin="5" Header="Fruit Info" >
<ComboBox Margin="5" ItemsSource="{Binding FruitInfo}" SelectedItem="{Binding SelectedFruit}"/>
</GroupBox>
Second: Make a SelectedFruit in your ViewModel
public string SelectedFruit { get; set; }
Ok, I understand what your trying to do, even though I'm still not sure why you're trying to do it.
The idea of using using is that it creates the variable for you, and the disposes of it when you finish the block of code you're running.
Now, you're creating a variable in that block, and return it ... and then, the system tries to dispose of it. So your return collection must be implicitly convertible to System.IDisposable, which I doubt yours is.
Putting that aside, you should follow emedbo advice. You will bind the source to the collection, and have another property for the selected index (since you're using mvvm).
I wouldn't get the data like that inside a using inside a getter, since it feels like that data you're getting might be deleted, and if it's not, then the whole use of your using is a bit wrong.
Not to mention it's not very readable, and you should aim for readability in most cases.
I don't use the Entity Framework, but I think the pattern for the FruitInfo property is missing of an important piece.
The problem is that the binding mechanism does not realize about the new ObservableCollection, because it expect some "notification" way to be alerted. That is, you have several ways to solve your problem:
use a DependencyPropety instead of an ordinary property: every time you set the property the bound controls are also notified.
I'd recommend this solution: reliable and versatile.
implement the INotifyPropertyChanged interface in the class exposing the FruitInfo property (e.g. MainWindow), then fire a PropertyChanged event on any actual FruitInfo's value changing.
This way is also valuable, but it looks useless adding a thing already exposed in any DependencyObject-derived class. The INotifyPropertyChanged fits perfectly for the POCO classes (Plain-Old CLR-Objects).
give a name to the combobox, then set the ItemsSource property explicitly.
It works fine, but you'd lose the benefits of the data-context inheritance, especially within templates.
the pattern you used creates the collection in a "lazy" fashion: consider avoiding the lazy-way, and set the FruitInfo value before the combobox is created/bound.
Doable, but typically may be applied in a few cases. Also requires that you know for sure the sequence of the objects creation. Keep as latest way.
== UPDATE
Try to modify your code as follows:
private ObservableCollection<string> _fruits = new ObservableCollection<string>();
public ObservableCollection<string> FruitInfo
{
get
{
using (var context = new Fruit())
{
this._fruits.Clear();
foreach (var item in context.Fruits.OrderBy(s => s.FruitName))
{
this._fruits.Add(item.FruitName);
}
return this._fruits;
}
}
}

C# - Listbox Key/value pair

I have a Dictionary, which contains data like so:
key Value
UK01 Building 1
UK02 Building 2
I have a textbox in which data is populated like this:
foreach (var building in dictionary)
{
listbox1.Items.Add(building.Value);
}
This then displays Building 1 Building 2, ...
Problem:
What I want to do is so that when the SelectionChanged is triggered, I can access the Key/Value for the option that has been selected and store these as variables which I can use later on. Currently I can only get the item selected, i.e "Building 1" or I can get the SelectedIndedbut this only gives me: 1, 2, .. And I understand why.
Is it therefore possible (elegantly) so that the key/value can be accessed without displaying the key? I have tried to use a class:
public class Test
{
public ID { get; set; }
public value { get; set; }
}
But this did not work. Does anyone have any suggestions?
Thank you :)
You need to use a DataTemplate in your ListBox to ensure the listbox knows how to render it's content. Assuming you follow your class based approach as above:
<ListBox>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding value}">
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
This creates a DataTemplate for each item you have added to the ListBox which contains a text block which is bound to a property on the items added
Read up on binding - it is one of the most powerful things in WPF/WP7/Silverlight and probably one of the most extensive implementations of binding out there
http://www.mono-software.com/blog/post/Mono/166/Data-binding-in-Windows-Phone-7-application/
Edit:
Though I'm pretty sure the default template for ListBox is just to call 'ToString()' on each item... so it should be working anyway!
Oh and consider making the members auto-properties instead of fields:
public string ID { get; set; }
Edit:
Ok I'm not great with Linq but since the query should return an IEnumerable<KeyValuePair<string, string>> as far as I can see, you can just enumerate it and build your objects off it e.g.
var myList = new ObservableCollection<MyClass>();
foreach(var kvp in dict)
{
myList.Add(new MyClass(kvp.Key, kvp.Value));
// Or myList.Add(new MyClass() { ID = kvp.Key, Value = kvp.Value }); depending on your constructor
}
Having said this, there no reason why you can't just point to the key/value of the dictionary items (KeyValuePair<string, string> is just a reference type like anything else)
This:
<ListBox>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Key}">
<TextBlock Text="{Binding Value}">
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Should give you
{Key}
{Value}
{Key}
{Value}
{Key}
{Value}
In your listbox
Assuming you don't need any additional functionality from the result items KeyValuePair should suffice. Creating a class just to show some values may be overkill since you are already 99% of the way there.
You might want to look at some MVVM patterns and maybe get into looking at an MVVM framework (I'm biased because I use it all the time but Caliburn.Micro is really easy to get started with and supports WP7).
Your code wouldn't be any more complex, but certain things would be wired up for you and it would give you more feedback as to where you are going wrong with bindings etc (some bindings are hard to figure out especially when popups/contextmenus are involved)
If you are serious about developing for XAML based technologies, they go hand in hand with the MVVM pattern, and a framework just makes things a cinch
If you are interested there's an easy to follow tut here:
https://caliburnmicro.codeplex.com/wikipage?title=Basic%20Configuration%2c%20Actions%20and%20Conventions&referringTitle=Documentation
Another variation on the same tut with a bit more info is here:
http://buksbaum.us/2010/08/01/caliburn-micro-hello-world/
I think you only need to override the ToString function in your Test class and it should work as you want it to
like this:
public override string ToString()
{
return Value;
}
I know WPF and somewhat it should be similar.
There you can bind a control to a collection with something called data binding.
http://msdn.microsoft.com/en-us/magazine/cc163299.aspx
http://blogs.msdn.com/b/wriju/archive/2011/07/25/windows-phone-7-binding-data-to-listbox-through-code.aspx

How can a WPF binding distinguish between an indexer property and a list element?

I have a binding of the form:
Path=SpecialCollection[0]
The SpecialCollection class extends ObservableCollection and has an indexer property.
public T this[string propertyValue]
{
get
{
// do stuff
return default(T);
}
}
My problem is that the binding attempts to get the indexer property value, instead of returning the 0th item in the collection. Is there a way to force the binding to treat 0 as an integer so it returns a collection element, instead of invoking the collection's indexer property's getter?
According to MSDN you can tell the binding the type of the value entered as index:
Inside indexers you can have multiple indexer parameters separated by commas (,). The type of each parameter can be specified with parentheses. For example, you can have Path="[(sys:Int32)42,(sys:Int32)24]", where sys is mapped to the System namespace.
I noticed that the Binding constructor taking a path string uses another PropertyPath constructor than the default PropertyPath type converter, said PropertyPath constructor does not work in this scenario. To avoid the problem avoid the Binding constructor by setting the Path property manually which invokes the conversion via type converter.
<!-- Does not work -->
<TextBlock Text="{Binding [(sys:Int32)0]}"/>
<!-- Does work -->
<TextBlock Text="{Binding Path=[(sys:Int32)0]}"/>
Actually you have two indexer properties, one that takes an int argument and one that takes a string argument. Frankly, I don't know how the binding expression chooses which indexer to use when it is ambiguous. If there is only one then it can coerce the index to the type of the indexer argument. If there are two, it can either throw an exception or choose one according to a heuristic. In this case, it apparently chose the wrong one.
To solve this problem you can either move your string indexer "down a level" so it hangs off of a new property so that it doesn't compete with the list indexer or if all you need is List[0] you can add a First property and bypass either indexer.

Categories