How to set fallback value for x:Static - c#

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.

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

Binding integer in XAML does not result in error

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.

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

Combobox Converter for Enumerations

I have an enumeration that has values like
HomeRun, StolenBase, FirstBase, etc.
I want to display these values in a combobox, with a space inserted before the capital letters, so it will display as 'Home Run', 'Stolen Base', etc.
I already have code that can do the formatting for me, and I have added that code to the 'Convert' method of an IValueConverter implementation.
My question is, where do I need to use this converter (in the xaml) such that not only the dropdown, but also the displayed value, will have this formatting? Do I need to implement ConvertBack as well?
I am well aware of setting 'descriptions' for the enumeration and using the popular EnumToDescriptionConverter, but I'd rather stay away from that.
I'm not sure if there is a better way, but you can achieve what you want using an ItemTemplate
<ComboBox.ItemTemplate>
<DataTemplate>
<ContentPresenter
Content="{Binding Converter={StaticResource baseballEnumConverter}}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
This will display the converted value in your ComboBox.
The SelectedValue of the ComboBox will still be the Enum value. You won't need to implement ConvertBack.
[updated] The key point of my answer is that the enum values are converted totally. I think this way is eaier than the coverting each enum value.[/updated]
Where do I need to use this converter (in the xaml) such that not only the dropdown, but also the displayed value, will have this
formatting?
At Binding ItemsSource of ComboBox(ItemsSource="{Binding Source={x:Null}, Converter={StaticResource converter}}"), Please check the following code.
Do I need to implement ConvertBack as well?
No, you don't., because at runtime you cannot modify the enumeration, and even though it can do, you cannot change ItemsSource of ComboBox in VIEW, which means Binding Mode is OneWay.
XAML
<Window x:Class="WpfApplication2.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfApplication2"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<local:MyEnumConverter x:Key="converter"/>
</Window.Resources>
<StackPanel>
<ComboBox ItemsSource="{Binding Source={x:Null}, Converter={StaticResource converter}, Mode=OneWay}"></ComboBox>
</StackPanel>
</Window>
Code
public enum MyEnum
{
HomeRun, StolenBase, FirstBase
}
[ValueConversion(typeof(object), typeof(List<string>))]
public class MyEnumConverter:IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var names = Enum.GetNames(typeof (MyEnum)).ToArray();
//Add some code to support the thing you want to do(add blank in front of Capital...)
return names;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
If you want the selected value of the ComboBox to be converted back to an enum then you will need to implement ConvertBack.
I'd personally go with the description attribute pattern that you mentioned, because
the obvious questions have already been asked, and
You aren't limited to simply inserting spaces at uppercase letters - you can use whatever description you want.
But assuming you want to go with this pattern, you just need to write your converter correctly. I'd suggest something like this:
// Convert method
var formattedNames = new List<string>();
foreach (var value in Enum.GetValues(typeof(Things)))
{
// Format is a method to convert the enum value to the display string
var formattedName = Format(value);
formattedNames.Add(formattedName);
}
// return a list of strings that you can bind to
return formattedNames;
// ConvertBack method
// Unformat is a method to revert the display string back to the enum value
var value = Unformat(formattedValue);
return Enum.Parse(typeof(Things), value);
You could also create a simple class to hold both the display value and the enum, and then set the DisplayPath property on the combo box appropriately
class DisplayEnum
{
public string DisplayValue { get;set; }
public MyEnum ActualValue { get;set; }
}
<ComboBox DisplayMemberPath=DisplayValue ...
Edit
I realise that this won't work because the ConvertBack is attempting to convert a string to an enum, but the actual binding set is a List<string>. I'll leave it here because it is a start in the right direction.
I believe you'd need two converters
to convert the enum type into a set of enum values, and
to convert an enum value to a string. This second converter should implement the ConvertBack method.
As I pointed out, if you don't implement ConvertBack then you won't be able to bind the SelectedValue back to your enum Property on your ViewModel.
You will need to make a dictionary or some other lookup structure that maps the Enum value to the string representation.
There is an hint that you can use as a start :
http://geekswithblogs.net/jawad/archive/2005/06/24/EnumDropDown.aspx
I developped my own enum binding helpers starting with that idea.

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