Data bound WPF ComboBox with choices defined in XAML? - c#

On my viewmodel I've got an int property and I want to expose it for editing with a ComboBox, with a limited set of choices, such as 16, 8, 4 and 2. Is there a way to specify the choices in the XAML, while still binding the value back to the viewmodel? I'd want to do something like this:
<ComboBox SelectedValue="{Binding MyIntProperty}">
<ComboBoxItem>16</ComboBoxItem>
<ComboBoxItem>8</ComboBoxItem>
<ComboBoxItem>4</ComboBoxItem>
<ComboBoxItem>2</ComboBoxItem>
</ComboBox>
I know I could rig up a List<int> in code and set that as the ItemsSource, but I'm hoping there's a way to do this that doesn't involve an extra property in the viewmodel that exposes a collection created in code.

You can specify your choices exactly as you are in your example. What it looks like your missing, to make it work, is the SelectedValuePath property. Without it, the SelectedValue would be the same as the SelectedItem. By setting SelectedValuePath="Content" in the ComboBox you can specify that your SelectedValue binding is instead binding to just a portion of the SelectedItem, in this case the Int content you specified as the content in each ComboBoxItem.
Here's a small demo with it, and also binding the value to a TextBox, where you can set the item and see it reflected in the ComboBox through the SelectedValue binding (or vice versa).
<StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Set Value:" />
<TextBox Text="{Binding MyIntProperty, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Select Value:" />
<ComboBox SelectedValue="{Binding MyIntProperty}" SelectedValuePath="Content">
<ComboBoxItem>2</ComboBoxItem>
<ComboBoxItem>4</ComboBoxItem>
<ComboBoxItem>6</ComboBoxItem>
<ComboBoxItem>8</ComboBoxItem>
<ComboBoxItem>16</ComboBoxItem>
</ComboBox>
</StackPanel>
</StackPanel>

Related

WPF -How to access the fields present in a template

Let me explain so I have a wpf application I used a listBox with a template. This template contains a TextBlock and a ComboBox. When running the application, everything goes well, my list is initialized correctly. But then I would like to retrieve the values ​​of my TextBlock and my comboBox and I don't understand how I can achieve this.
I am attaching the part of my XAML code that deals with this listBox :
<ListBox x:Name="ListBoxEnv" Grid.Row="1"
d:ItemsSource="{d:SampleData ItemCount=5}" Width="460"
SelectionChanged="ListBoxEnv_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock x:Name="TxtBlockEnv"
VerticalAlignment="Center" HorizontalAlignment="Left"
Text="{Binding EnvName}"/>
<ComboBox x:Name="ComboBoxEnv"
VerticalAlignment="Center" HorizontalAlignment="Left"
ItemsSource="{Binding EnvListValue}"
Margin="100,2,0,2" Width="200"
SelectionChanged="ComboBoxEnv_SelectionChanged"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The EnvName property is readonly because it is bound to TextBlock, so you can ignore it. Change binding to: Text="{Binding EnvName, Mode=OneTime}" to save the app resources.
However to extract selected environment in every combo in the list you need to add to ComboBox template: SelectedItem={Binding SelectedEnv} and add new property to SampleData
for example
public MyEnvironmentClass SelectedEnv {get; set;}

How to bind a form in a grid that is bound to a tree which is bound to a different collection in wpf

Ok so this is kinda hard to explain. I have a view which has a tree and has a form. The tree is bound to 2 different observable collections.
<TreeView FontFamily="Calibri" FontSize="16" Name="tree" Margin="3" Grid.Row="1" Grid.Column="0" Height="950" VerticalAlignment="Top" Grid.ColumnSpan="2"
ItemsSource="{Binding Path=Org, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" SelectedItemChanged="tree_SelectedItemChanged" >
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=Org_Sredstva}">
<TextBlock Text="{Binding Path=Organizacije_Naziv}"/>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Ossr_Naziv}"/>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
Now then next to the tree there is a form. And the form is supposed to be bound to a third observable collection that changes depending on selecteditem of tree.
<Grid Grid.Row="2" Grid.Column="2" Margin="5" DataContext="{Binding ElementName=tree, Path=SelectedItem}">
As you can see the binding is to the tree which is bound to a different observable collection.
<extToolkit:WatermarkTextBox x:Name="RadnoVrijeme" Grid.Row="1"
Grid.Column="1"
Grid.ColumnSpan="2"
HorizontalAlignment="Left"
VerticalAlignment="Bottom"
Width="100"
Height="25"
Padding="3"
Margin="3"
AcceptsReturn="True"
Validation.ErrorTemplate="{StaticResource ValidationTemplate}"
Validation.Error="Validation_Error">
<extToolkit:WatermarkTextBox.Watermark>
<StackPanel Orientation="Horizontal">
<TextBox Text="Radno Vrijeme" Margin="4,2,2,0" FontWeight="Regular" Foreground="Silver" BorderThickness="0"/>
</StackPanel>
</extToolkit:WatermarkTextBox.Watermark>
<extToolkit:WatermarkTextBox.Text>
<Binding Path="Opr_RadnoVrijeme" UpdateSourceTrigger="PropertyChanged" NotifyOnValidationError="True">
<Binding.ValidationRules>
<validation:InputLenghtRule MinLength="10" MaxLength="250" ValidatesOnTargetUpdated="True"/>
</Binding.ValidationRules>
</Binding>
</extToolkit:WatermarkTextBox.Text>
</extToolkit:WatermarkTextBox>
This is a textbox and it is bound to a property on the third collection but it doesn't work obviously because that collection is never set as itemssource anywhere.
The workaround I found is to in codebehind set every textbox individually so
this.View.RadnoVrijeme.Text = opr.Opr_RadnoVrijeme;
Where the left side of the equation is the text property of the textbox and the right side is the collection.property. This works but I don't like it and was hoping there was a way to actually bind this?
You should definetely have a look at MVVM. It utilize modular design, where your View is consisting of multiple Views controlled by ViewModels. Then it's easy to solve any kind of dependency (relation? selection?) problem.
In your case you are trying to bind to SelectedItem directly, while in MVVM you will handle selection and then you have possibility to rise notification of related property change, which will cause update of corresponding View element automatically.
Here is a generic example:
ItemVM _selectedItem;
public ItemVM SelectedItem
{
get { return _selectedItem; }
set
{
_selectedItem = value;
OnPropertyChanged();
// trigger update in the view
OnPropertyChanged(nameof(SomeRelatedProperty));
}
}
// bind TextBlock.Text to that property
public string SomeRelatedProperty
{
get
{
// some logic based on selected item
if(SelectedItem.SomeProperty == SomeValue)
return "a";
...
return "b";
}
}
The cooler thing if SelectedItem already provides value as one of property (because it's a ViewModel), then assuming all ViewModels implement INotifyPropertyChanged you can simply bind to that property directly and whenever selected item is changed the view will be updated to display that property too:
<TextBlock Text="{Binding SelectedItem.SomeProperty} ... / >
As for error you are getting:
System.Windows.Data Error: 40 : BindingExpression path error: 'Opr_RadnoVrijeme' property not found on 'object' ''Organizacije' (HashCode=23451234)'. BindingExpression:Path=Opr_RadnoVrijeme; DataItem='Organizacije' (HashCode=23451234); target element is 'WatermarkTextBox' (Name='RadnoVrijeme'); target property is 'Text' (type 'String')
Try binding (pretty unsure, you didn't provide ViewModels to figure that out):
<WatermarkTextBox Text="{Binding SelectedItem.Opr_RadnoVrijeme, RelativeSource={RelativeSource FindAncestor, AncestorType=TreeView}}" ... />

Show Font-Style in a Combobox in it's own style

I have two comboboxes.
for FontFamily.
for FontWeight.
Xaml for 1st Combobox looks like:
<ComboBox IsEditable="True"
ItemsSource="{x:Static Fonts.SystemFontFamilies}" >
<ComboBox.ItemTemplate>
<DataTemplate DataType="{x:Type FontFamily}">
<TextBlock Text="{Binding}" FontFamily="{Binding}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
In the output of the above XAML I can see all the font-names in its own style. I want to do something similar for 2nd combobox also. Currently I have some items in 2nd combobox as below:
<ComboBox IsEditable="True">
<x:Static Member="FontStyles.Normal"/>
<x:Static Member="FontStyles.Italic"/>
<x:Static Member="FontStyles.Oblique"/>
</ComboBox>
How can I show each item in above combobox in its own style using Combobox.ItemTemplate or something similar without styling each item.
For e.g. my output should look something like:
Normal
Italic
Oblique
Take advantage of type converters: for most properties there's a converter that converts a string to a suitable value for the property. It's needed to be able to parse XAML (which is all strings) to types (think of writing something like Width="Auto" while remembering that Width is a double value).
So, you can use something like this:
<ComboBox>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding }"
FontStyle="{Binding }" />
</DataTemplate>
</ComboBox.ItemTemplate>
<system:String>Normal</system:String>
<system:String>Italic</system:String>
<system:String>Oblique</system:String>
</ComboBox>
The binding for FontStyle sets a string and then type converter springs into action and converts the string to an actual FontStyle value to be used by the property.
NOTE: this might not work in .NET 3.0/3.5
EDIT : just remembered, in .NET 3.0/3.5, if there's a converter defined for the binding, then type converter is not working - the binding expects the converter to return the right type for the property. Not sure if it was changed in .NET 4.0/4.5 (probably not, and ,IMHO, shouldn't have - need to check it to verify).
Oh, and add this xmlns: xmlns:system="clr-namespace:System;assembly=mscorlib"

Bind to Parent Object Property with RelativeSource

I've built a WPF based Treeview with
Item
-Subitem
If Subitem is selected, I would like to display also Properties of Item.
<StackPanel Grid.Column="2" DataContext="{Binding ElementName=myTreeView, Path=SelectedItem}">
<TextBox Text="{Binding Path=Name, Mode=TwoWay}" />
<TextBox Text="{Binding RelativeSource={???} Path=Name, Mode=TwoWay}" />
</StackPanel>
I guess I need to use a RelativeSource statement, but not quite sure how to do so.
{Binding RelativeSource={RelativeSource AncestorType={x:Type typeOfAncestor}}, Path=Name, Mode=TwoWay}
JesseJames has given you the correct way to use RelativeSource but the best you will be able to do with RelativeSource is bind to the TreeViewItem itself, which is just the container for your data object i.e ViewModel, meaning you won't be able to access your data objects properties(easily).
I think in this case binding to the container object would break the View-ViewModel approach you are using. Your best bet would be to create a Parent object within your ViewModel and bind to that object. So that now each object in your collection has a reference to it's parent which can now be bound to directly.
<StackPanel Grid.Column="2" DataContext="{Binding ElementName=myTreeView, Path=SelectedItem}">
<TextBox Text="{Binding Path=Name, Mode=TwoWay}" />
<TextBox Text="{Binding Parent.Name}" />
</StackPanel>
Also note that the SelectedItem property returns your data object and not the container.
I looked at your code, try binding simply to Name. It appears that your data context should already be set to the TreeViewItem due to the following line: <StackPanel Grid.Column="2" DataContext="{Binding ElementName=myTreeView, Path=SelectedItem}">. The RelativeResource binding is probably looking further up the logical tree and that's why your binding is failing.

Windows phone 7.1, Listpicker fullmodeitemtemplate binding text

i'm trying to change the font size for my items in a listpicker.
I use a fullmodeitemtemplate to be able to change fontsize etc.
The problem is that i have no idea how to bind the text for the items in the template
<DataTemplate x:Name="PickerFullModeItemTemplate">
<StackPanel Orientation="Horizontal" Margin="16 21 0 20" Background="Orange" Width="110" Height="110" HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock Text="{WHAT TO TYPE HERE?}" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="5" FontSize="36"/>
</StackPanel>
</DataTemplate>
I populate my listpicker by setting the itemsource in C# like this
foreach (Item i in weight)
{
sourceInput.Add(i.name);
}
}
InputSelector.ItemsSource = sourceInput;
This leaves me with an itemsource list only containing strings, then i dont know how to bind the text for each item. I read some post on how to do it when the itemsource list is in this format
source.Add(new Cities() { Name = "Mexico", Country = "MX", Language = "Spanish" });
this.listPicker.ItemsSource = source;
and then the xaml part is something like this
<TextBlock Text="{Binding Name}"/>
any help would be greatly appreciated :)
UPDATE
i found the correct binding for binding to the sourceitems.
<TextBlock Text="{Binding BindsDirectlyToSource=True}"/>
Looks like thats the way to go, then the source items get binded to the textblock
You should be adding objects of type Cities to your sourceInput collection.
Typing Text="{Binding Name}" is correct.
Most likely your Cities class just doesn't implement the INotifyPropertyChanged interface. You should notify the UI each time you update the Name and other properties you have bound your UI elements to.
<TextBlock Text="{Binding Name}"/>
it is cool but for the listpicker there is also DisplayMemberPath property so,
on listpicker add :
DisplayMemberPath="Name"

Categories

Resources