I have this combobox in my view:
<ComboBox SelectedValue="{Binding StringObj, UpdateSourceTrigger=PropertyChanged}">
<ComboBoxItem>string0</ComboBoxItem>
<ComboBoxItem>string1</ComboBoxItem>
<ComboBoxItem>string2</ComboBoxItem>
<ComboBoxItem>string3</ComboBoxItem>
</ComboBox>
And in my view model I have this string object:
private string _stringObj;
public string StringObj
{
get { return _stringObj; }
set { _stringObj = value; }
}
How can I bind the selected value from the combobox to the string variable so I can work with it in the view model? This is what I have implemented so far but it doesn't work as I don't understand this binding stuff very well.
You can't set a string property to a ComboBoxItem value.
You could either replace the ComboBoxItems with strings in your XAML and bind the SelectedItem property:
<ComboBox SelectedItem="{Binding StringObj, UpdateSourceTrigger=PropertyChanged}"
xmlns:s="clr-namespace:System;assembly=mscorlib">
<s:String>string0</s:String>
<s:String>string1</s:String>
<s:String>string2</s:String>
<s:String>string3</s:String>
</ComboBox>
Or you could simply set the SelectedValuePath property to "Content":
<ComboBox SelectedValue="{Binding StringObj, UpdateSourceTrigger=PropertyChanged}"
SelectedValuePath="Content">
<ComboBoxItem>string0</ComboBoxItem>
<ComboBoxItem>string1</ComboBoxItem>
<ComboBoxItem>string2</ComboBoxItem>
<ComboBoxItem>string3</ComboBoxItem>
</ComboBox>
This will set your source property to the Content of the selected ComboBoxItem.
Related
I bind an ObservableCollection to a ComboBox and set the DisplayMemberPath to the Name,
with the help of SelectedValuePath I wanted to achieve that Device.ObjectId gets the value of Object.Id.
But this doesn't seem to work, the Object.Name is still set for Device.ObjectId.
How do I get the ComboBox to show the Name but set the Id?
public class Object
{
public string Id { get; set; }
public string Name { get; set; }
}
ObservableCollection Objects = new ObservableCollection<Object>();
<ComboBox ItemsSource="{Binding Objects}" DisplayMemberPath="Name" SelectedValuePath="Id">
<ComboBox.Text>
<Binding Path="Device.ObjectId" UpdateSourceTrigger="PropertyChanged">
</ComboBox.Text>
</ComboBox>
Since you set the SelectedValuePath attribute to Id, you can bind to the SelectedValue attribute to get the value. Something like this:
<ComboBox ItemsSource="{Binding Objects}" DisplayMemberPath="Name"
SelectedValuePath="Id" SelectedValue="{Binding Device.ObjectId}" />
In my View I have a DataGrid which stores objects of 2 descending types. Every row has a Button with a Command connected to the ViewModel. In the ViewModel I need to find out which type of object has been chosen.
The question is what is the best and simple way of accessing SelectedItem property of the DataGrid from the Execute command method in a ViewModel?
So far I did it like this:
var window = Application.Current.Windows.OfType<Window>()
.SingleOrDefault(x => x.IsActive);
var dataGrid = (DataGrid) window.FindName("MyGridName");
...
UPDATE - Xaml:
<DataGrid Name="MyGridName" ItemsSource="{Binding Elements}"
AutoGenerateColumns="False" CanUserAddRows="False"
CanUserDeleteRows="False" IsReadOnly="True">
<DataGrid.Columns>
<DataGridTemplateColumn Width="auto">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Name="OptionsBtn" Margin="5" Width="auto"
Height="30" Content="Options"
Command="{Binding ElementName=ElementsViewWindow,
Path=DataContext.ShowOptionsMenuCommand}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
If you take the right MVVM approach this is very easy to do. All you need is to define the item collection of you entities which will be bound to ItemsSource of your DataGrid and a property which will be bound to SelectedItem of your DataGrid. Then in your command you simply reference your selected item property of your model to access the selected item in your DataGrid.
Here is an example implementation with MVVM Light. First you define an observable collection of your entities:
public const string ItemsCollectionPropertyName = "ItemsCollection";
private ObservableCollection<DataItem> _itemsCollection = null;
public ObservableCollection<DataItem> ItemsCollection
{
get
{
return _itemsCollection;
}
set
{
if (_itemsCollection == value)
{
return;
}
_itemsCollection = value;
RaisePropertyChanged(ItemsCollectionPropertyName);
}
}
Then you define a property to hold the selected item:
public const string SelectedItemPropertyName = "SelectedItem";
private DataItem _selectedItem = null;
public DataItem SelectedItem
{
get
{
return _selectedItem;
}
set
{
if (_selectedItem == value)
{
return;
}
_selectedItem = value;
RaisePropertyChanged(SelectedItemPropertyName);
}
}
After that you define a command to handle business logic:
private ICommand _doWhateverCommand;
public ICommand DoWhateverCommand
{
get
{
if (_doWhateverCommand == null)
{
_doWhateverCommand = new RelayCommand(
() => { /* do your stuff with SelectedItem here */ },
() => { return SelectedItem != null; }
);
}
return _doWhateverCommand;
}
}
Finally you create view elements and bind them to the ViewModel:
<DataGrid ItemsSource="{Binding ItemsCollection}" SelectedItem="{Binding SelectedItem}" AutoGenerateColumns="True" />
<Button Content="Do stuff" Command="{Binding DoWhateverCommand}" />
The question is what is the best and simple way of accessing SelectedItem property of DataGrid from Execute command function in a ViewModel?
Just add a property to the view model class where the ShowOptionsMenuCommand property is defined and bind the SelectedItem property of the DataGrid to this one:
<DataGrid Name="MyGridName" ItemsSource="{Binding Elements}" SelectedItem="{Binding SelectedElement}" ... >
Then you can access the source property (SelectedElement or whatever you choose to call it) directly from the Execute method.
The other option would be to pass the item as a CommandParameter to the command:
<Button Name="OptionsBtn" ... Content="Options"
Command="{Binding ElementName=ElementsViewWindow, Path=DataContext.ShowOptionsMenuCommand}"
CommandParameter="{Binding}" />
What I am trying to do is take the string out of a specific column in my DataGrid and display that string in the Combobox.Text property. I am using this code to do so:
var dataString = ((DataRowView)dgvMain.SelectedItem).Row["Column"].ToString();
I have placed a breakpoint on this to see what it was pulling, and it was the correct String that is contained as an item in the Combobox, but whenever I try to set it via Combobox.Text, the Combobox is empty. I have however set my Combobox to isEditable = True and ReadOnly = True and this method works, but selecting an item in the Collection will display System.Data.DataRowView, due to the Combobox being binded to my DataTable. I'll add the XAML to my Combobox as well:
<ComboBox x:Name="cboAlarmType" HorizontalAlignment="Left" Margin="138,256,0,0" VerticalAlignment="Top" Width="320" TabIndex="5"
BorderBrush="Black" Background="White" ItemsSource="{Binding}" DisplayMemberPath="AlarmName" SelectedValuePath="AlarmName"
SelectedValue="{Binding Row.Column, ElementName=dgvMain}"/>
This method works in Textboxes and Checkboxes, but haven't seemed to figure it out for Comboboxes. Any guidance would be appreciated! :)
It appears to me that your DataContext for the ComboBox must be the DataTable you're using to populate the DataGrid, because ItemsSource="{Binding}". I'm going to assume that it's the DataContext for the DataGrid as well, and that your goal is to have the ComboBox selection reflect the selected row in the DataGrid.
<DataGrid
x:Name="dgvMain"
ItemsSource="{Binding}"
/>
<ComboBox
ItemsSource="{Binding}"
DisplayMemberPath="Column"
SelectedValuePath="Column"
SelectedValue="{Binding SelectedItem.Row[Column], ElementName=dgvMain}"
/>
If you'd like the selection changes to go both ways -- so a change to the ComboBox selection changes the DataGrid selection -- that's easy:
<DataGrid
x:Name="dgvMain"
ItemsSource="{Binding}"
SelectedValuePath="Column"
SelectedValue="{Binding SelectedValue, ElementName=ColumnComboBox}"
/>
<ComboBox
x:Name="ColumnComboBox"
ItemsSource="{Binding}"
DisplayMemberPath="Column"
SelectedValuePath="Column"
SelectedValue="{Binding SelectedItem.Row[Column], ElementName=dgvMain}"
/>
And here's a cleaner way to do the whole thing:
First, write a viewmodel class in C#.
public class ViewModelbase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class MyViewModel : ViewModelbase
{
public MyViewModel()
{
// I populated my DataTable with "Row 1", "Row 2", etc.
// Naturally you'd use a value from your own data.
SelectedColumnValue = "Row 2";
}
#region SelectedColumnValue Property
private String _selectedColumnValue = default(String);
public String SelectedColumnValue
{
get { return _selectedColumnValue; }
set
{
if (value != _selectedColumnValue)
{
_selectedColumnValue = value;
OnPropertyChanged();
}
}
}
#endregion SelectedColumnValue Property
public DataTable Data { get; protected set; }
}
Then use that SelectedColumnValue property to keep track of the selected value of "Column" for both controls. The bindings on the SelectedValue properties of the two controls will be two-way by default, because of framework stuff that makes it be that way (how's that for an explanation?). So if you change SelectedColumnValue programatically, the controls will update, and if the user changes the grid selection, the viewmodel property will be updated, which will in turn update the ComboBox -- and vice versa.
<DataGrid
ItemsSource="{Binding Data}"
SelectedValuePath="Column"
SelectedValue="{Binding SelectedColumnValue}"
/>
<ComboBox
ItemsSource="{Binding Data}"
DisplayMemberPath="Column"
SelectedValuePath="Column"
SelectedValue="{Binding SelectedColumnValue}"
/>
<ComboBox
ItemsSource="{Binding Data}"
DisplayMemberPath="ShoeSize"
SelectedValuePath="Column"
SelectedValue="{Binding SelectedColumnValue}"
/>
Another wrinkle is that DisplayMemberPath and SelectedValuePath needn't be the same. Say we've got a "ShoeSize" column in Data, and we'd like to have those values displayed in another ComboBox. So we can do that. We're still using Column as a unique identifier for rows in Data, but we can tell the ComboBox to display some other column.
I have View and its ViewModel. In View, I have some Panels that has binding to some properties in my ViewModel;
For example first property is SomeObject. In that panel I have some textboxes, that has binding to some properties in SomeObject class. All bindings in ViewModel and classes implements INPC.
That textbox, in panel, has binding to its property in SomeObject class this way: SomeObject.Property.
In that view I have a Save button. I need change the IsEnabled property of my button every time when something was entered in textboxes or changed in panel.
I've done it only for SomeObject property in my viewmodel by changing IsEnabled property bound to button from SomeObject's setter. But when I change something in properties of my SomeObject it doesn't call the setter of main SomeObject in ViewModel, and I can't change my IsEnabled property from SomeObject class.
<controls:ExpandableSettingsPanel Header="REFUND" IsExpanded="{Binding RefundCustomerConfigurationEnabled, Mode=TwoWay}" ..>
<StackPanel..>
<CheckBox Content="Allowed within reversal period" IsChecked="{Binding RefundCustomerConfiguration.IsAllowedWithinReversalPeriod, Mode=TwoWay}"/>
<TextBlock Style="{DynamicResource GrayTextBlockStyle}" Text="Minimal amount to be refunded"/>
<WrapPanel>
<TextBlock Text="€" .. />
<controls:MementoTextBox Text="{Binding RefundCustomerConfiguration.MinimalAmountToBeRefunded, Mode=TwoWay,
NotifyOnValidationError=True, StringFormat=\{0:N2\},
TargetNullValue='',
UpdateSourceTrigger=PropertyChanged,
ValidatesOnNotifyDataErrors=True,
ValidatesOnExceptions=True}" ..>
<i:Interaction.Behaviors>
<behaviors:TextBoxInputRegExBehavior
EmptyValue="0.00"
IgnoreSpace="True"
RegularExpression="^\d{1,2}(\.?\d{1,2})?$"
MaxLength="6"
/>
</i:Interaction.Behaviors>
</controls:MementoTextBox>
</WrapPanel>
<TextBlock Style="{DynamicResource GrayTextBlockStyle}" Text="Minimal bank statement line age" ../>
<controls:MementoTextBox Text="{Binding RefundCustomerConfiguration.MinimalBankStatementLineAge,
Mode=TwoWay,
NotifyOnValidationError=True,
ValidatesOnNotifyDataErrors=True,
UpdateSourceTrigger=PropertyChanged,
ValidatesOnExceptions=True }" ..>
<i:Interaction.Behaviors>
<behaviors:TextBoxInputRegExBehavior
EmptyValue="0"
IgnoreSpace="True"
RegularExpression="^\d{1,3}$"
MaxLength="3"
/>
</i:Interaction.Behaviors>
</controls:MementoTextBox>
</StackPanel>
</controls:ExpandableSettingsPanel>
This is my XAML part.
public RefundCustomerConfiguration RefundCustomerConfiguration
{
get { return _refundCustomerConfiguration; }
set
{
SetProperty(ref _refundCustomerConfiguration, value);
OnPropertyChanged("RefundCustomerConfigurationEnabled");
}
}
[Required(ErrorMessage = "This field is required")]
public bool IsAllowedWithinReversalPeriod
{
get { return GetValue(() => IsAllowedWithinReversalPeriod); }
set
{
SetPropertyValue(value);
}
}
And this is my properties; First - in ViewModel. Second - in first property class.
Just bind button's visibility property to the Visibility method in the class which realize INotifyPropertyChanged interface as like this:
public Visibility btnVisible
{
get
{
if(<your condition>)
return Visibility.Visible;
else
return Visibility.Collapsed;
}
}
I want to bind my combo box ItemsSourse to the "value" i.e. (string component) of the
ObservableCollection<KeyValuePair<object, string>>.
How can I do that?
You could bind the ItemsSource to the ObservableCollection and then set the DisplayMemberPath to Value:
<ComboBox ItemsSource="{Binding YourCollection}" DisplayMemberPath="Value" />
The values in the combo box will then match the Values from the KeyValuePairs.
The easiest way to go would be to use the DisplayMemberPath property:
<ComboBox ItemsSource="{Binding Pairs}" DisplayMemberPath="Value" />
Alternatively, you could expose a new property in your viewmodel that will only contain the values. For example:
public ObservableCollection<string> AllValues { get; set; }
public ViewModel()
{
AllValues = new ObservableCollection<string>(Pairs.Select(x => x.Value));
}
<ComboBox ItemsSource="{Binding AllValues}" />