In the following xaml fragment SessoList is a list of string ("M" and "F").
<ComboBox IsEditable="False" Margin="5" SelectedValue="{Binding Sesso}" ItemsSource="{Binding SessoList}" Width="40" Height="28"/>
The combobox works as expected and it is pre-populated reflecting the value of Sesso in the viewmodel.
The combobox selectable items are only two and fixed so I tried to simplify defining them in xaml:
<ComboBox IsEditable="False" Margin="5" SelectedValue="{Binding Sesso}" SelectedValuePath="{Binding Tag}" Width="40" Height="28" Name="Primo">
<ComboBoxItem Content="M" Tag="M" />
<ComboBoxItem Content="F" Tag="F" />
</ComboBox>
This combobox is capable of updating the viewmodel property sesso, but is not pre-populated with the correct value.
The following error is reported:
BindingExpression path error: 'Tag' property not found on 'object'
How can I successfully define the combobox items in xaml and have it display the right value based on SelectedValue databinding ?
Forgot to mention I'm using .Net 4.0
As I can understand, you want to define the ComboBox ItemsSource in XAML,
here is the solution that worked for me:
Xaml Window resources:
<Window.Resources>
<x:Array x:Key="Array" Type="{x:Type nirHelpingOvalButton:ComboObjectModel}">
<nirHelpingOvalButton:ComboObjectModel Content="M_Content" Tag="M_Tag"/>
<nirHelpingOvalButton:ComboObjectModel Content="F_Content" Tag="F_Tag"/>
</x:Array>
Xaml Combo:
<Grid>
<ComboBox IsSynchronizedWithCurrentItem="True" IsEditable="False" SelectedIndex="0" Margin="5" ItemsSource="{StaticResource Array}"
SelectedValue="{Binding Content, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
SelectedValuePath="Tag" Width="90" Height="28" Name="Primo">
<i:Interaction.Behaviors>
<nirHelpingOvalButton:CustomComboSelectionBehavior/>
</i:Interaction.Behaviors>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Content}"></TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox></Grid>
View model combo SelectedValue binded property:
public string Content
{
get { return _content; }
set
{
_content = value;
OnPropertyChanged("Content");
}
}
List item Behavior Code:
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Loaded += OnLoaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
var firstItem = AssociatedObject.ItemsSource.Cast<object>().FirstOrDefault();
AssociatedObject.SelectedItem = firstItem;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.Loaded -= OnLoaded;
}
regards
Related
I have a ComboBox which is Binded to a List<String> defined in the ViewModel.
I have as well a ListBox who's ItemSource depends on SelectedValue from the ComboBox.
In the ViewModel, I have the following properties:
List<String> ComboBoxSource
string SelectedValue (ComboBox selected value)
ObservableCollection<String> ListBoxSource
Now, when the ComboBox selectedvalue changes, I am setting the SelectedVAlue property in the ViewModel(DataContext) explicitly which is raising a PropertyChange Event on ListBoxSource in order to update the ListBox.
My question is how can I do this without explicitly setting SelectedVAlue in ViewModel i.e Can I bind the ComboBox.SelectedVAlue to my SelectedVAlue property ?
Here is my XAML:
<ComboBox Grid.Column="0" Grid.Row="0" x:Name="ComboBoxVersions" SelectedIndex="0" Margin="10" SelectionChanged="ComboBoxVersions_OnSelectionChanged" ItemsSource="{Binding EnvironmentVersions}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Version " />
<TextBlock Text="{Binding}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<ListBox x:Name="ListBoxEnvironments" Grid.Column="0" Grid.Row="1" Height="300" Grid.ColumnSpan="2" HorizontalAlignment="Stretch" Margin="10" SelectionMode="Multiple" ItemsSource="{Binding Environments}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" HorizontalAlignment="Left" Width="800" >
</WrapPanel>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox x:Name="CheckBoxEnvironment" Content="{Binding}" IsChecked="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}, Path=IsSelected}" Margin="5">
</CheckBox>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Here is the Code Behind:
/// <summary>
/// Handles the OnSelectionChanged event of the ComboBoxVersions control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="SelectionChangedEventArgs"/> instance containing the event data.</param>
private void ComboBoxVersions_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
var dataContext = this.DataContext as TestRunnerControlViewModel;
dataContext.SelectedVersion = ((ComboBox) sender).SelectedValue.ToString();
}
Use SelectedItem instead of SelectedValue because it is more appropriate to bind, just create a SelectedVersion property, bind this property to the combobox's SelectedItem, whenever the SelectedItem is changed, update you ListBox itemSource in the setter of that property.
INotifyPropertyChanged interface is necessary to let the ListBox know that their source has been changed
private List<String> _environmentVersions;
public List<String> EnvironmentVersions
{
get
{
return _environmentVersions;
}
set
{
if (_environmentVersions == value)
{
return;
}
_environmentVersions = value;
OnPropertyChanged();
}
}
private ObservableCollection<String> _environments ;
public ObservableCollection<String> Environments
{
get
{
return _environments;
}
set
{
if (_environments == value)
{
return;
}
_environments = value;
OnPropertyChanged();
}
}
private String _selectedVersion ;
public String SelectedVersion
{
get
{
return _selectedVersion;
}
set
{
if (_selectedVersion == value)
{
return;
}
_selectedVersion = value;
//update your listBox itemSource
// ...
}
}
and in the Xaml:
<ComboBox Grid.Column="0" Grid.Row="0" x:Name="ComboBoxVersions" SelectedIndex="0" Margin="10" SelectedItem="{Binding SelectedVersion}" ItemsSource="{Binding EnvironmentVersions}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Version " />
<TextBlock Text="{Binding}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
I have created an integer array in XAML as follows:
<x:Array Type="{x:Type sys:Int16}" x:Key="intervals">
<sys:Int16>1</sys:Int16>
<sys:Int16>2</sys:Int16>
<sys:Int16>3</sys:Int16>
</x:Array>
I have an integer property in ViewModel named Interval.
I am trying to bind this Interval property to the SelectedValue property of the ComboBox. However, the ComboBox does not show the selected value corresponding to the value of the Interval property. This is my ComboBox XAML code:
<ComboBox SelectedValue="{Binding Path=Interval}" Height="25" ItemsSource="{StaticResource intervals}" HorizontalContentAlignment="Center" Margin="78,47,237,128" Name="comboBox1" VerticalContentAlignment="Center" Width="85" />
Hi you should try SelectedItem instead of SelectedValue because for SelectedVlue you also have to provide SelectedValuePath.
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
public Int16 Interval { get; set; }
private void Button_Click(object sender, RoutedEventArgs e)
{
var a = Interval;
}
}
<Window.Resources>
<x:Array Type="{x:Type sys:Int16}" x:Key="intervals">
<sys:Int16>1</sys:Int16>
<sys:Int16>2</sys:Int16>
<sys:Int16>3</sys:Int16>
</x:Array>
</Window.Resources>
<Grid x:Name="LayoutRoot">
<StackPanel>
<ComboBox SelectedItem="{Binding Path=Interval}" Height="25" ItemsSource="{StaticResource intervals}" HorizontalContentAlignment="Center" Margin="78,47,237,128" Name="comboBox1" VerticalContentAlignment="Center" Width="85" />
<Button Content="ok" Click="Button_Click"/>
</StackPanel>
</Grid>
I hope this will help.
You created an array of short values. Your Interval property should then also be of type short.
public short Interval { get; set; }
If you intended to use int as item type, you would have to use sys:Int32 as the array element type.
Also, you should bind the SelectedItem property. Although it works in your case, SelectedValue is meant to work in conjunction with SelectedValuePath.
<ComboBox SelectedItem="{Binding Path=Interval}" ... />
I have a WPF user control that contains a DataGrid. This DG contains several columns including a ComboBox for states. The list of states is populated and stored as a property in my ViewModel.
I am trying to bind the StateList Property to the ItemsSource of my Combobox but when I run the form and try to edit the DG, the combobox does not contain any values, the combobox is empty.
Here is the XAML for the usercontrol.
<UserControl x:Class="myproject.View.ucContactView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" d:DesignHeight="475" d:DesignWidth="977">
<UserControl.Resources>
<ResourceDictionary Source="/Templates/MyResourceDictionary.xaml"/>
</UserControl.Resources>
<Grid DataContext="{Binding ViewModel}">
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding AddressCollectionViewSource.View}">
<DataGridTemplateColumn Header="State" Width="160">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding StateDescription}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<ComboBox Name="cboState"
SelectedValuePath="StateKey"
ItemTemplate="{StaticResource dtStateTemplate}"
ItemsSource="{Binding StateList}"
SelectedItem="{Binding StateKey, Mode=TwoWay}"
Width="100" />
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
</DataGrid>
</Grid>
</UserControl>
The odd thing is that if I create another combobox on this usercontrol with the exact same combobox, this combobox works as expected.
<!-- this works as long as it's not in the DG -->
<StackPanel Height="126" HorizontalAlignment="Left" Margin="766,275,0,0" Name="stackPanel1" VerticalAlignment="Top" Width="200" >
<ComboBox Name="cboState2"
SelectedValuePath="StateKey"
ItemTemplate="{StaticResource dtStateTemplate}"
ItemsSource="{Binding StateList}"
SelectedItem="{Binding StateKey, Mode=TwoWay}"
Width="100" />
</StackPanel>
Why won't the combobox in the DG display the values from the StateList property? Any why does the separate combobox work properly?
It's not working because your ComboBox is looking for StateList as a property of the DataContext of the DataGrid. That is, it's trying to bind to ViewModel.AddressCollectionViewSource.View.StateList when it needs to be binding to ViewModel.StateList. Check your output window while debugging and I bet you'll see a binding error to the effect of Could not find property StateList on object AddressCollectionViewSource (or maybe ICollection).
Try this instead:
<ComboBox Name="cboState2"
SelectedValuePath="StateKey"
ItemTemplate="{StaticResource dtStateTemplate}"
ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type DataGrid}}, Path=DataContext.StateList}"
SelectedItem="{Binding StateKey, Mode=TwoWay}"
Width="100" />
if your viewmodel is a property at the window you can do this
ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}, Path=ViewModel.StateList, Mode=OneWay}"
<Window x:Class="WpfStackOverflowSpielWiese.Window2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window2"
Height="300"
Width="300"
x:Name="window">
<Grid DataContext="{Binding ElementName=window, Path=ViewModel}">
<DataGrid x:Name="grid"
AutoGenerateColumns="False"
ItemsSource="{Binding AddressCollectionViewSource, Mode=OneWay}">
<DataGrid.Columns>
<DataGridTemplateColumn Header="State"
Width="160">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding StateKey}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<ComboBox Name="cboState"
SelectedValuePath="StateKey"
ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}, Path=ViewModel.StateList, Mode=OneWay}"
SelectedItem="{Binding StateKey, Mode=TwoWay}"
Width="100" />
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
using System.Collections.ObjectModel;
using System.Windows;
namespace WpfStackOverflowSpielWiese
{
/// <summary>
/// Interaction logic for Window2.xaml
/// </summary>
public partial class Window2 : Window
{
public static readonly DependencyProperty ViewModelProperty =
DependencyProperty.Register("ViewModel", typeof(ViewModelClass), typeof(Window2), new PropertyMetadata(default(ViewModelClass)));
public ViewModelClass ViewModel {
get { return (ViewModelClass)this.GetValue(ViewModelProperty); }
set { this.SetValue(ViewModelProperty, value); }
}
public Window2() {
this.InitializeComponent();
this.grid.Items.Clear();
this.ViewModel = new ViewModelClass();
}
}
public class StateClass : DependencyObject
{
public static readonly DependencyProperty StateKeyProperty =
DependencyProperty.Register("StateKey", typeof(string), typeof(ViewModelClass), new PropertyMetadata(default(string)));
public string StateKey {
get { return (string)this.GetValue(StateKeyProperty); }
set { this.SetValue(StateKeyProperty, value); }
}
public static readonly DependencyProperty StateProperty =
DependencyProperty.Register("State", typeof(string), typeof(StateClass), new PropertyMetadata(default(string)));
public string State {
get { return (string)this.GetValue(StateProperty); }
set { this.SetValue(StateProperty, value); }
}
}
public class ViewModelClass : DependencyObject
{
public static readonly DependencyProperty StateListProperty =
DependencyProperty.Register("StateList", typeof(ObservableCollection<string>), typeof(ViewModelClass), new PropertyMetadata(default(ObservableCollection<string>)));
public static readonly DependencyProperty AddressCollectionViewSourceProperty =
DependencyProperty.Register("AddressCollectionViewSource", typeof(ObservableCollection<StateClass>), typeof(ViewModelClass), new PropertyMetadata(default(ObservableCollection<StateClass>)));
public ObservableCollection<StateClass> AddressCollectionViewSource {
get { return (ObservableCollection<StateClass>)this.GetValue(AddressCollectionViewSourceProperty); }
set { this.SetValue(AddressCollectionViewSourceProperty, value); }
}
public ObservableCollection<string> StateList {
get { return (ObservableCollection<string>)this.GetValue(StateListProperty); }
set { this.SetValue(StateListProperty, value); }
}
public ViewModelClass() {
this.StateList = new ObservableCollection<string>(new[] {"one", "two"});
this.AddressCollectionViewSource = new ObservableCollection<StateClass>(new[] {new StateClass {State = "state", StateKey = "one"}});
}
}
}
I want to do with xaml bindings such feature:
Listbox contains hyperlinks.
When hyperlink clicked - we go to another frame
But also SelectedItem must changed, and on another frame we show info about selected item.
I want it without subscribing click/selected events. Only declarative
Example of my listbox
<ListBox Grid.Row="1"
x:Name="OrderTypesListBox"
ItemsSource="{Binding OrderTypes, Mode=OneWay}"
SelectedItem="{Binding SelectedCall.OrderType, Mode=TwoWay}"
>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}" />
<HyperlinkButton Style="{StaticResource LinkStyle}" NavigateUri="/WindowPage" TargetName="ContentFrame" Content="WindowPage"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Now solve like that
<ListBox Grid.Row="1"
x:Name="OrderTypesListBox"
ItemsSource="{Binding OrderTypes, Mode=OneWay}"
SelectedItem="{Binding SelectedCall.OrderType, Mode=TwoWay}"
>
<ListBox.ItemTemplate>
<DataTemplate>
<HyperlinkButton
TargetName="ContentFrame"
NavigateUri="{Binding OrderTypeNextPage}"
Content="{Binding Name}"
Click="HyperlinkButton_Click"
Tag="{Binding}"
/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
private void HyperlinkButton_Click(object sender, RoutedEventArgs e)
{
OrderTypesListBox.SelectedItem = (sender as HyperlinkButton).Tag;
}
Don't use a HyperlinkButton. Perform the needed actions when the SelectedItem changes in your ViewModel.
Edit: If you need to respond to all click events even if the item is already selected then you can use a Behavior to do this. Just create a behavior for the TextBlock that navigates on the TextBlock click event.
Edit2: Behaviors are pretty simple to code up and easy to use (and don't break the MVVM paradigm).
public class NavigatingTextBlockBehavior : Behavior<TextBlock>
{
protected override void OnAttached()
{
AssociatedObject.MouseLeftButtonDown += new MouseButtonEventHandler(OnMouseDown);
}
private void OnMouseDown(object sender, MouseButtonEventArgs e)
{
NavigationService.Navigate(new Uri("/WindowPage"));
}
}
I am trying to declaratively bind a ComboBox within a DataGrid CellEditingTemplate using a ViewModel. The ComboBox is not being bound. What am I doing wrong?
XAML:
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ed="http://schemas.microsoft.com/expression/2010/drawing"
xmlns:data="clr-namespace:SilverlightApplication1"
mc:Ignorable="d"
x:Class="SilverlightApplication1.EmployeeDetail"
Width="640" Height="480">
<UserControl.Resources>
<data:EmployeeDetailsViewModel
x:Key="ViewModel"
d:IsDataSource="True" />
</UserControl.Resources>
<Grid x:Name="LayoutRoot" DataContext="{Binding Source={StaticResource ViewModel}}" Background="White">
<sdk:DataGrid ItemsSource="{Binding Employees,Mode=TwoWay}" AutoGenerateColumns="False" CanUserSortColumns="True" CanUserReorderColumns="True" CanUserResizeColumns="True" GridLinesVisibility="All" Height="317" HorizontalAlignment="Left" Margin="12,136,0,0" Name="EmployeesGrid" VerticalAlignment="Top" Width="605">
<sdk:DataGrid.Columns>
<!-- snipped from brevity -->
<sdk:DataGridTemplateColumn Header="Status">
<sdk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding EmployeeStatus.Description}" TextWrapping="Wrap"></TextBlock>
</DataTemplate>
</sdk:DataGridTemplateColumn.CellTemplate>
<sdk:DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Path=EmployeeStatuses}" SelectedItem="{Binding EmployeeStatus, Mode=TwoWay}" />
</DataTemplate>
</sdk:DataGridTemplateColumn.CellEditingTemplate>
</sdk:DataGridTemplateColumn>
</sdk:DataGrid.Columns>
</sdk:DataGrid>
<TextBlock x:Name="SearchLabel" HorizontalAlignment="Left" Margin="12,95,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="106" Height="34"><Run FontWeight="Bold" Text="Search By Name: "/><Run FontSize="9.333" Text="(Last, First)"/></TextBlock>
<TextBox x:Name="SearchParam" HorizontalAlignment="Left" Margin="144,101,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="162"/>
<Button x:Name="SearchButton" Content="Search" HorizontalAlignment="Right" Margin="0,102,242,0" VerticalAlignment="Top" Width="75" Click="SearchButton_Click"/>
</Grid>
</UserControl>
VIEW MODEL:
using System.Collections.ObjectModel;
using SilverlightApplication1.EmployeeService;
using SilverlightApplication1.ViewModels;
namespace SilverlightApplication1
{
public class EmployeeDetailsViewModel : ViewModelBase
{
readonly IEmployeeServiceAgent _serviceAgent;
ObservableCollection<EmployeeStatus> _employeeStatuses { get; set; }
ObservableCollection<Employee> _employees { get; set; }
public EmployeeDetailsViewModel() : this(new EmployeeServiceAgent()) { }
public EmployeeDetailsViewModel(IEmployeeServiceAgent serviceAgent)
{
if (!IsDesignTime)
{
_serviceAgent = serviceAgent;
GetAllEmployees();
GetEmployeeStatuses();
}
}
public ObservableCollection<Employee> Employees
{
get { return _employees; }
set
{
if(_employees!=value)
{
_employees = value;
OnNotifyPropertyChanged("Employees");
}
}
}
public ObservableCollection<EmployeeStatus> EmployeeStatuses
{
get { return _employeeStatuses; }
set
{
if (_employeeStatuses != value)
{
_employeeStatuses = value;
OnNotifyPropertyChanged("EmployeeStatuses");
}
}
}
private void GetAllEmployees()
{
_serviceAgent.GetAll((s, e) => Employees = e.Result);
}
private void GetEmployeeStatuses()
{
_serviceAgent.GetEmployeeStatuses((s, e) => EmployeeStatuses = e.Result);
}
}
}
Update:
This seems wrong but I figured out how to get the binding working by re-referencing the ViewModel in the ItemSource Binding:
<ComboBox ItemsSource="{Binding Source={StaticResource ViewModel},Path=EmployeeStatuses}"
DisplayMemberPath="Description"
SelectedItem="{Binding EmployeeStatus, Mode=TwoWay}" />
However, a am now experiencing a problem where the SelectedItem is not bound! What am I doing wrong?
The problem is a common one that people run into. When you're in the data template of the column, you're no longer bound the the view model. At that point your data context is the EmployeeStatus object (which doesn't have an EmployeeStatuses property to bind to).
So to get the combobox binding to work you can use the ElementName=LayoutRoot to bind back up the tree to the root ViewModel.
Update: Here would be the full syntax for your binding:
{Binding DataContext.EmployeeStatuses, ElementName=LayoutRoot}
Update2: I've actually run into this as well and there is a workaround you have to implement to get the element name binding to work inside a datagrid.
If Bryant's solution does not work (in SL4), use static resources. See this link: http://blog.digitaltools.com/post/2011/05/06/Binding-a-Datagride28099s-ComboBox.aspx
Or, by creating the static resource in xaml: http://forums.silverlight.net/post/370135.aspx