Binding integer in XAML does not result in error - c#

I am a newbie to WPF/XAML. I would like to get an error message if I bind to the wrong data type in XAML. It seems that XAML wants all binding to be through strings, but there are no error messages if you use an int or double by mistake.
I have found this XAML code here:
<ItemsControl ItemsSource="{Binding Path=PointList}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<!--<TextBox Text="{Binding Path=Xstr, Mode=OneWay}" />-->
<Rectangle Fill="Red" Width="25" Height="25" />
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Top" Value="{Binding Path=Ystr}" />
<Setter Property="Canvas.Left" Value="{Binding Path=Xstr}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
I have used an observable collection PointList of Points(X,Y). I made the mistake at first of using integers for X and Y instead of strings. This was very difficult to debug since there was no error message when trying to bind the Canvas.Top to an integer. Is there a setting in Visual Studio to be able to catch this kind of error?
UPDATE
I have found that the binding works on an int property, but not with a public int field. Here is a Point class I created to test this out:
class Point
{
public int _X; //I know this is usually private, this is to demonstrate
public int _Y; //the issue with binding to a public field
public string Xstr
{
get { return _X.ToString(); }
}
public string Ystr
{
get { return _Y.ToString(); }
}
public int X
{
get { return _X; }
private set { _X = value; }
}
public int Y
{
get { return _Y; }
private set { _Y = value; }
}
public Point(int x, int y)
{
_X = x;
_Y = y;
}
}
If I bind to the int property X or string property Xstr it works fine. If I try to use the public field _X then it seems the binding cannot find the class member (even though it is public). So when a binding fails, the behavior is not the same as an exception in your code. An error like the following shows up in the output window, but the application does not stop:
System.Windows.Data Error: 40 : BindingExpression path error: '_X' property not found on 'object' ''Point' (HashCode=7877106)'. BindingExpression:Path=_X; DataItem='Point' (HashCode=7877106); target element is 'ContentPresenter' (Name=''); target property is 'Left' (type 'Double')

seems that XAML wants all binding to be through strings
That's not true. You can bind which ever datatype you would like, as long as the property you are trying to bind to and the property you are trying to bind are from the same type, or share a certain base type.
If they are not, WPF Binding mechanism offers something called Converters which lets you convert the bounded type to the type the binded property is expecting.
By default, WPF Binding mechanism will call the ToString() method on any property you are trying to bind. in your case, calling a ToString() method on an int returns the wanted result, so no problem there.
But if you were to use a reference type for example, by default ToString() would return the type's name instead of the value you were expecting, which would cause an unexpected behaviours.
You can not catch those type of "errors".
You can :
See them at run-time in visual studio output window, as mentioned in the comments section.
System.Windows.Data Error: 5 : Value produced by BindingExpression is not valid for target property
Pay attention to what you are binding
Look for type-mismatches when seeing unexpected behaviours in bindings.
Welcome to the magic of xaml :)

A data binding can use any data type. The binding system calls "ToString" or built-in converters on each item to get a representation for XAML. (You get around that by writing your own converters, which is a XAML subject all its own.)
The others are correct that .NET doesn't throw errors when there are problems with binding. This is, I've been told by the Microsoft employees who helped write it, intentional. They call it "failing gracefully".
But getting error message feedback is possible. To get better error messaging from the binding system, find the "Tools", "Options" menu item in Visual Studio. Then, go to the "Debugging" section and select the "Output Window" property category.
One of the entries there is a group called "WPF Trace Settings". Change the "Data Binding" option to "Warning" and you'll get debug-window feedback on all your failed bindings.

Firstly using X and Y as integer instead of string actually work.
Otherwise if you set you project as debug instead of realease, most binding error will be printed in the console output.

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;
}
}
}

Databinding to entries in a dictionary with enums as keys

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 }">

Silverlight 3/Prism - Passing an enum value as a Command Parameter

I'm trying to use Prism and the MVVM pattern to develop an application. In my UI, I have a previous and next button defined. For use in a calling web services, I've defined an enum that will tell me the direction I need to traverse. So, in this instance, the buttons map directly to an enum value. The enum definition is very simple and is as follows:
namespace CodeExpert.Book.Helpers
{
public enum BookDirection { Previous = -1, NotSet = 0, Next = 1, }
}
I've defined my command and delegate in my ViewModel and assigned the propery correctly. The relevant code is:
public DelegateCommand PreviousNextCommand { get; set; }
public IndexEntriesViewModel(GlobalVariables globalVariable, IndexEntryOperations currentOperator)
{
//a bunch of initialization code.
InitializeCommands();
}
void InitializeCommands()
{
PreviousNextCommand =
new DelegateCommand(OnPreviousNextCommandExecute);
}
private void OnPreviousNextCommandExecute(BookDirection parameter)
{
//Code to process based on BookDirection
}
So, based on this config, I want to pass a BookDirection enum value to the CommandParameter. I can't, however, get the XAML right for this. Here's the XAML I've tried that seems the most correct to me:
<UserControl 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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="CodeExpert.Book.Views.Index"
d:DesignWidth="1024"
d:DesignHeight="768"
xmlns:helpers="clr-namespace:CodeExpert.Book.Helpers"
xmlns:command="clr-namespace:Microsoft.Practices.Composite.Presentation.Commands;assembly=Microsoft.Practices.Composite.Presentation"
xmlns:common="clr-namespace:System.Windows;assembly=System.Windows.Controls"
xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows"
xmlns:input="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Input">
<Button x:Name="ButtonPrevious"
HorizontalAlignment="Left"
Margin="2,1,0,1"
Width="25"
Content="<"
Grid.Column="1"
Grid.Row="1"
Height="20"
command:Click.Command="{Binding Path=CurrentIndexViewModel.PreviousNextCommand}">
<command:Click.CommandParameter>
<helpers:BookDirection.Previous />
</command:Click.CommandParameter>
</Button>
</UserControl>
I get no intellisense for the BookDirection for the enum and get an error at design & compile time saying The type 'BookDirection' does not contain adefinition for 'Previous'. Is there no way to pass that enum, or am I simply missing something? I have it working now by setting the parameter type to string instead of BookDirection, but then I have to parse text and the code smells. I've done some Google searches and the closest thing I've come to an answer is here - Passing an enum value as command parameter from XAML Unfortunately, Silverlight doesn't support the x:static binding extension, so I can't use the exact technique described in that answer.
Any help would be much appreciated.
Just pass command parameter like simple object type. Then you can use direct cast to your enum.
Binding enums are really a troublesome operation, even in WPF. But there seems to be an elegant solution to it, which is available at the Caliburn framework.
The solution, however, is not available in the framework code, but in it's LOB Samples. Look for the BindableEnum and BindableEnumCollection<T> classes on the Caliburn.Silverlight.ApplicationFramework project.
HTH.
First, I am not sure if you can pass an Enum directly from XAML in Silverlight. In anycase here is a little problem in Prism. See the Command is getting set before the CommandParameter, what happens when the command is getting set internally Prism calls UpdateEnabledState(), which internally calls CanExecute(object parameter) on your DelegateCommand passing it the CommandParameter ( which remember has not been set yet )
here is the basic code from DelegateCommand.cs in Prism
bool ICommand.CanExecute(object parameter)
{
return CanExecute((T)parameter);
}
since parameter is "null" at this point, the cast throws an exception. Here is how I got around this issue.
Your Enum.
namespace CodeExpert.Book.Helpers
{
public enum BookDirection { Previous = -1, NotSet = 0, Next = 1, }
}
here is my delegate declaration, note the use of object..rather than the Enum itself. I also expose properties from the ViewModel which will expose the 2 different directions.
public DelegateCommand<object> PreviousNextCommand { get; set; }
public BookDirection Previous { get { return BookDirection.Previous; }}
public BookDirection Next { get { return BookDirection.Next; }}
now in your OnPreviousNextCommandExecute make sure you receive an object and cast it back to the proper enum
private void OnPreviousNextCommandExecute(object parameter)
{
BookDirection direction = (BookDirection)parameter;
//Code to process based on BookDirection
}
and in XAML bind directly to the exposed BookDirection properties.
<Button Content="Next" Commands:Click.Command="{Binding PreviousNextCommand}" Commands:Click.CommandParameter="{Binding Next}" />
<Button Content="Previous" Commands:Click.Command="{Binding PreviousNextCommand}" Commands:Click.CommandParameter="{Binding Previous}" />
I am not sure about your binding situation as in my case I set my DataContext directly to the ViewModel. But this should work for you.
Sorry for my poor English and eloquence, hope this puts you on the right track.
I haven't tried it myself but you may have some success to use a ValueConverter from something like string to your enum. That was the impression I got from looking around for enums in Silverlight xaml.

Categories