When I wrote "Admin" in textbox I want to change backcolor of label that its content is binding to class propery as in the below:
<Window.Resources>
<local:TextToColorConverter x:Key="TextToColorConverterDataSource" d:IsDataSource="True"/>
<local:Class1 x:Key="Class1DataSource" d:IsDataSource="True"/>
</Window.Resources>
<Grid DataContext="{Binding Source={StaticResource Class1DataSource}}">
<Label x:Name="label" Content="{Binding FullName, Mode=OneWay}" Height="26.463" Margin="77.951,23.512,232.463,0" VerticalAlignment="Top" Background="{Binding Content, Converter={StaticResource TextToColorConverterDataSource},UpdateSourceTrigger=PropertyChanged}"/>
<TextBox x:Name="textBox1" Height="29.878" Margin="77,80,200,0" TextWrapping="Wrap" Text="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top" HorizontalAlignment="Left"/>
</Grid>
As I said above label content is binding to a class propery also textbox text is binding to class property.The code is:
class Class1 : INotifyPropertyChanged
{
#region Properties
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged("Name");
OnPropertyChanged("FullName");
}
}
private string _fullname;
public string FullName
{
get { return string.Format("{0}", _name); }
set
{
_fullname = value;
OnPropertyChanged("FullName");
}
}
#endregion
#region INotifyPropertyChanged Implementing
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
Label's background is binding to another class and it is converting text to color:
public class TextToColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value.ToString() == "Admin")
return new SolidColorBrush(Colors.Yellow);
else
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Label content data binding is working well but background data binding not working...(When I binding to textbox's text to label background is working but I wondered How can I make this way)
The problem is with the Background Binding
Background="{Binding Content, Converter= ...}"
As written, you're binding to a property Content on the Grid's DataContext, which doesn't exist.
You could either bind to the right property on the Grid's DataContext:
Background="{Binding FullName, Converter= ...}"
Or bind to the Content property on the Label using RelativeSource:
Background="{Binding Content, RelativeSource={RelativeSource Self}, Converter= ...}"
Related
I'm working on an UWP application. Which has a GridView with following structure:
<Page.Resources>
<local:boolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
</Page.Resources>
<GridView ItemsSource="{x:Bind ExampleItems, Mode=OneWay}" x:Name="mDataGridView" ItemClick="mDataGridView_ItemClick" IsItemClickEnabled="True">
<GridView.ItemTemplate>
<DataTemplate x:Name="DataTemplate" x:DataType="local:ItemTemplate">
<StackPanel Height="100" Width="100" Background="OrangeRed" x:Name="rootPanel">
<TextBlock x:Name="TitleTextBlock" Text="{x:Bind Title,Mode=OneWay}"/>
<TextBlock Text="{x:Bind Subtitle,Mode=OneWay}" />
<TextBlock Text="{x:Bind Description,Mode=OneWay}" />
<ProgressBar Visibility="{x:Bind ShowProgress, Converter={StaticResource BoolToVisibilityConverter},Mode=OneWay}" />
</StackPanel>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
And a quite simple corresponding item data class:
public class ItemTemplate
{
public string Title { get; set; }
public string Subtitle { get; set; }
public string Description { get; set; }
public bool ShowProgress { get; set; }
}
A converter to convert the "ShowProgress" property into Visibility:
public class boolToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
bool show = (bool)value;
return show ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
Visibility visibility = (Visibility)value;
return visibility.Equals(Visibility.Visible);
}
}
The code works fine to display the GridView when the application starts. But if I try to change the the progress bar visibility by changing corresponding "ShowProgress" property when the application is running, the view won't update.
ExampleItems[15].ShowProgress = true;
ExampleItems[15].Title = "New Title 15";
Any one got any idea for how to change the visibility with x:bind mechanism? Any suggestion would be appreciated. Thank you.
Alex
You need to implement the INotifyPropertyChanged interface and fire the PropertyChanged event whenever property values change.
for E.g :
public class ItemTemplate :INotifyPropertyChanged
{
private bool _showProgress;
public bool ShowProgress
{
get { return _showProgress; }
set
{
_showProgress = value;
RaisePropertyChanged("ShowProgress");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
For the complete code listing, see the XAML data binding sample.
I created a ComboBox listing the colors that System.Windows.Media.Colors predefines, using the approach told in this question: How can I list colors in WPF with XAML?
My XAML code now is:
<Window ...>
<Window.Resources>
<ObjectDataProvider
ObjectInstance="{x:Type Colors}" MethodName="GetProperties" x:Key="ColorList" />
<local:StringToBrushConverter x:Key="FontColorConversions" />
</Window.Resources>
<Grid Background="Black">
...
<ComboBox Grid.Column="1" Grid.Row="1" Height="22" Width="240"
VerticalAlignment="Center" HorizontalAlignment="Left"
ItemsSource="{Binding Source={StaticResource ColorList}}"
SelectedValue="{Binding FontColor, Mode=TwoWay}"
DisplayMemberPath="Name"
SelectedValuePath="Name">
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem">
<Setter Property="Foreground" Value="{Binding Converter={StaticResource FontColorConversions}}"/>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
...
</Grid>
</Window>
And besides, please note that I bind SelectedValue to a VM class's FontColor property, which is of string type.
class FontSetting : INotifyPropertyChanged
{
private string _fontColor = "Lavender"; // initial color
public event PropertyChangedEventHandler PropertyChanged;
public string FontColor
{
get
{
return _fontColor;
}
set
{
_fontColor = value;
OnPropertyChanged("FontColor");
}
}
protected virtual void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
And I set the DataContext of the Window containing this ComboBox to an instance of FontSetting.
So each item in the ComboBox actually display a string representing a certain color now, what I want to do is set an item's Foreground color to that color its content indicates, like this:
Can anyone help? Thanks.
UPDATED:
Since most of the solutions have a converter which converts string to Brush and actually I already have it, now I want to put mine here, as I binded a TextBox's Foreground to FontSetting's FontColor property, so that when you change the ComboBox, the color of that TextBox changes accordingly.
Here is my converter class, and it works fine by now:
class StringToBrushConverter : IValueConverter
{
public object Convert(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
BrushConverter conv = new BrushConverter();
SolidColorBrush brush = conv.ConvertFromString("Lavender") as SolidColorBrush;
if (null != value)
{
brush = conv.ConvertFromString(value.ToString()) as SolidColorBrush;
}
return brush;
}
public object ConvertBack(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}
When I click the ComboBox to open the dropdown list, I got an exception:
CONCLUSION
Amine's solution works, it's my mistake. I explain briefly now, if you bind a ComboBox to System.Windows.Media.Colors like what I am doing, when the item is rendered, the Convert() method of the converter class (which you assign to the binding) is executed, and actually the value passed to Convert() as its first parameter is a Syetem.Windows.Media.Color instance. I made mistake coz I thought it was of string type.
Therefore, in my case I need two converter classes, one converting string to Brush, and the other one converting Color to Brush. So I will keep my own StringToBrush converter and add Amine's ColorToBrush converter.
However, I simplified Amine's implementation a bit:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
BrushConverter conv = new BrushConverter();
SolidColorBrush brush = SolidColorBrush)conv.ConvertFromString(FontSetting.DEFAULT_FONT_COLOR);
if (null != value)
{
PropertyInfo pi = value as PropertyInfo;
if (null != pi)
{
brush = conv.ConvertFromString(pi.Name) as SolidColorBrush;
}
}
return brush;
}
Moreover, Joe's input is also valuable, put all them together, I can keep the items' color consistent, which is perfect.
You can set Style of ComboBoxItem as bellow :
<ComboBox Grid.Column="1" Grid.Row="1" Height="22" Width="240" x:Name="CB"
VerticalAlignment="Center" HorizontalAlignment="Left"
ItemsSource="{Binding Source={StaticResource colorPropertiesOdp}}"
DisplayMemberPath="Name"
SelectedValuePath="Name">
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem">
<Setter Property="Foreground" Value="{Binding Converter={StaticResource converter}}"/>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
By using this converter:
public class MyConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
object obj = ((System.Reflection.PropertyInfo)value).GetValue(this,null);
return (SolidColorBrush)new BrushConverter().ConvertFromString(obj.ToString());
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
return value;
}
}
Two ways, use a value converter or an intermediate property. The easiest is probably an intermediate property as you are using well structured bindings already for your SelectedItem (but value converters are fun too!).
SelectedValue is bound to FontColor, so in your setter set another value:
public string FontColor
{
get
{
return _fontColor;
}
set
{
_fontColor = value;
ForegroundColorToDisplay = GetBrushFromColorString(value);
OnPropertyChanged("FontColor");
}
}
public Brush _foregroundColorToDisplay
public Brush ForegroundColorToDisplay
{
get
{
return _foregroundColorToDisplay;
}
set
{
_foregroundColorToDisplay= value;
OnPropertyChanged("ForegroundColorToDisplay");
}
}
or, if you don't want to store it:
public string FontColor
{
get
{
return _fontColor;
}
set
{
_fontColor = value;
//note it fires two changed events!
OnPropertyChanged("ForegroundColorToDisplay");
OnPropertyChanged("FontColor");
}
}
public Brush ForegroundColorToDisplay
{
get
{
return GetBrushFromColorString(value);;
}
}
You can bind to this new property in your xaml:
<ComboBox Grid.Column="1" Grid.Row="1" Height="22" Width="240"
VerticalAlignment="Center" HorizontalAlignment="Left"
ItemsSource="{Binding Source={StaticResource colorPropertiesOdp}}"
SelectedValue="{Binding FontColor, Mode=TwoWay}"
DisplayMemberPath="Name"
SelectedValuePath="Name"
Foreground="{Binding ForegroundColorToDisplay, Mode=OneWay}"/>
If you are interested, a value converter would work like this:
Create a class in your code behind (or elsewhere if it's going to be used a lot) that implements IValueConverter, takes the string and returns a Brush:
public class StringToBrushConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
return GetBrushFromString(value as string)
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
Throw new SomeException();
}
}
and use it in your binding in xaml to convert the selectedItem binding to Foreground brush:
<Window.Resources>
<local:StringToBrushConverter x:Key="converter" />
</Window.Resources>
...
<ComboBox Grid.Column="1" Grid.Row="1" Height="22" Width="240"
VerticalAlignment="Center" HorizontalAlignment="Left"
ItemsSource="{Binding Source={StaticResource colorPropertiesOdp}}"
SelectedValue="{Binding FontColor, Mode=TwoWay}"
DisplayMemberPath="Name"
SelectedValuePath="Name"
Foreground="{Binding FontColor, Mode=OneWay, Converter={StaticResource converter}}"/>
I'm a true beginner in MVVM pattern. I'm trying to change the backgound of a grid on button's click. I have a xaml with a grid containing a button, and a ViewModel .cs from where i want to change the grid's background on button's click. Until there i just succeed to show up a MessageBox when i clicked...
.xaml code:
<Window x:Class="WpfSimple.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfSimple"
Title="MainWindow" Height="150" Width="370">
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<Button Content="Click"
Height="23"
HorizontalAlignment="Left"
Background="Gray"
Margin="75.944,47.465,0,0"
Name="btnClick"
VerticalAlignment="Top"
Width="203"
Command="{Binding ButtonCommand}"/>
<!--What is necessary to add for changing grid color ? Commandparameter ?-->
</Grid>
MainWindowViewModel.cs code:
namespace WpfSimple
{
class MainWindowViewModel
{
private ICommand m_ButtonCommand;
public ICommand ButtonCommand
{
get
{
return m_ButtonCommand;
}
set
{
m_ButtonCommand = value;
}
}
public MainWindowViewModel()
{
ButtonCommand=new RelayCommand(new Action<object>(ChangeBgColor));
}
public void ChangeBgColor(object obj)
{
/*HERE I WANT TO CHANGE GRID COLOR*/
}
}
}
Sorry for my bad english.
Best regards.
Fitst of all you should implement INotifyPropertyChanged in your ViewModel:
public class MainWindowViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
// This method is called by the Set accessor of each property.
// The CallerMemberName attribute that is applied to the optional propertyName
// parameter causes the property name of the caller to be substituted as an argument.
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Then, add NotifyPropertyChanged() to your properties setter.
Ok. Next, add new Property with your grid background color to your ViewModel:
private Brush _gridBackground;
public Brush GridBackground
{
get { return _gridBackground; }
set
{
_gridBackground = value;
NotifyPropertyChanged();
}
}
And bind your grid's background to your property:
<Grid Background="{Binding GridBackground}">
Finally you can just change GridBackground in the command handler:
public void ChangeBgColor(object obj)
{
GridBackground = Brushes.Blue;
}
You should remember that it's a bad practice to add WPF-classes like Brush to your code. The better way is to use IValueConverter in XAML code and BCL classes in your ViewModel. For example, you can use enumeration in ViewModel and convert it to brush in ValueConverter.
Add new enum for your ViewModel's property:
public enum GridState { Valid, Invalid }
Change property type:
private GridState _gridBackground;
public GridState GridBackground
{
get { return _gridBackground; }
set
{
_gridBackground = value;
NotifyPropertyChanged();
}
}
Add new class with value converter
public class GridStateToBackgroundColorConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
GridState val = (GridState) value;
if(val == GridState.Valid)
return Brushes.Green;
return Brushes.Red;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
#endregion
}
Add new static resource to your control
<UserControl.Resources>
<converters:GridStateToBackgroundColorConverter x:Key="gridStateToBackgroundColorConverter" />
</UserControl.Resources>
Update binding to your property
<Grid Background="{Binding GridBackground, Converter={StaticResource gridStateToBackgroundColorConverter}">
If you want to change the grid background color then you can use command parameter. You can pass any UI control as Command parameter. In your case pass grid to access grid in your view model.
Give the name to your grid and use that name to use as command parameter.
For this you need to implement the code like this:
<Window x:Class="WpfSimple.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfSimple"
Title="MainWindow" Height="150" Width="370">
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<Grid Name="grid">
<Button Content="Click"
Height="23"
HorizontalAlignment="Left"
Background="Gray"
Margin="75.944,47.465,0,0"
Name="btnClick"
VerticalAlignment="Top"
Width="203"
Command="{Binding ButtonCommand}"
CommandParameter="{Binding Elementname="grid"}"/>
</Grid>
After making this change to the .xaml file. Implement Parameterized Relay Command to use this passed Grid to use in your Viewmodel file.
To Implement Parameterized Relay Command Try to Implement following code:
private ICommand m_ButtonCommand;
public ICommand ButtonCommand
{
get
{
return m_ButtonCommand;
}
set
{
m_ButtonCommand = value;
}
}
public MainWindowViewModel()
{
ButtonCommand=new RelayCommand<Grid>(ChangeBgColor);
}
public void ChangeBgColor(Grid grid)
{
if(grid!=null)
grid.Background = Brushes.Red; //Any color you want to change.
}
I hope this will work. Thank you.
Im having text box and list view and when you are pressing on the button
you are the list view is filled with data ,currently the list view is under the button and the text box and
always is there and filled after you press on the button.
There is a way to hide the list view from the page until you press on the button and requesting the data?
public class ModelView
{
public ModelView()
{
GetServiceCollection = new ObservableCollection<string>();
}
bool isDataLoaded = false;
MyCommand goCommand;
public ICommand GoCommand
{
get { return goCommand ?? (goCommand = new MyCommand(() => OnGoCommand(), () => !isDataLoaded)); }
}
public ObservableCollection<string> GetServiceCollection { get; set; }
void OnGoCommand()
{
GetServiceCollection.Clear();
foreach (var item in _configServiceModel.CollectList)
{
GetServiceCollection.Add(item);
}
isDataLoaded = true;
goCommand.RaiseCanExecuteChanged();
}
......
The xaml
<Button Content="Go" Grid.Column="3" Grid.Row="1" HorizontalAlignment="Left"
VerticalAlignment="Top" Width="75" Height="21.96" Command="{Binding GoCommand}"/>
<ListView Grid.Column="2" HorizontalAlignment="Center" Height="230"
Margin="5,20,0,0" Grid.Row="2" VerticalAlignment="Top" Width="330"
ItemsSource="{Binding GetCollection}" }" >
}
ViewModel
public class ConfigModelView:INotifyPropertyChanged
{
public ConfigModelView()
{
GetServiceCollection=new ObservableCollection<string>();
}
bool isDataLoaded;
public bool IsDataLoaded
{
get { return isDataLoaded; }
set { isDataLoaded = value; OnPropertyChanged("IsDataLoaded"); }
}
MyCommand goCommand;
public ICommand GoCommand
{
get{return goCommand ?? (goCommand=new MyCommand(()=>Command(),()=>!isDataLoaded));}
}
public ObservableCollection<string> GetServiceCollection{get;set;}
void Command()
{
foreach (var item in _configServiceModel.CollectList)
{
GetServiceCollection.Add(item);
}
isDataLoaded = true;
OnPropertyChanged("IsDataLoaded");
goCommand.RaiseCanExecuteChanged();
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
BooleanToVisibilityConverter
public class BoolToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is bool)
{
if ((bool)value)
return Visibility.Visible;
else
return Visibility.Collapsed;
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
xaml
<Window x:Class="WpfApplication3.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication3"
Title="Window1" Height="300" Width="800">
<Window.Resources>
<local:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
</Window.Resources>
<StackPanel>
<Button Content="Go" Grid.Column="3" Grid.Row="1" HorizontalAlignment="Left"
VerticalAlignment="Top" Width="75" Height="21.96" Command="{Binding GoCommand}"/>
<ListView Grid.Column="2" HorizontalAlignment="Center" Height="230"
Margin="5,20,0,0" Grid.Row="2" VerticalAlignment="Top" Width="330"
Visibility="{Binding IsDataLoaded,
Converter= {StaticResource BoolToVisibilityConverter}}"
ItemsSource="{Binding GetCollection}" />
</StackPanel>
The best bet here would be to create another property on the ViewModel that you bind the Visibility of the ListView to. In the GoCommand implementation, set this property to visible.
As a side note, your ViewModel doesn't implement INotifyPropertyChanged, so you'll need to do that as well to have the visiblity update when the property is changed:
private Visibility listViewVisibility;
public Visibility ListViewVisibility
{
get { return listViewVisibility; }
set
{
if (this.listViewVisibility == value)
return;
this.listViewVisibility = value;
this.OnPropertyChanged("ListViewVisibility");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if(this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
The xaml:
<ListView Grid.Column="2" HorizontalAlignment="Center" Height="230"
Margin="5,20,0,0" Grid.Row="2" VerticalAlignment="Top" Width="330"
Visibility="{Binding ListViewVisibility}"
ItemsSource="{Binding GetCollection}" />
Well load your form then in the put "listView1.hide()".
Then create your button event.
Type "listView1.show()".
P.S. you can also set all of those values in your c# code.
I have a UserControl in which I have a DataDrid and in that DataGrid I have two ComboBoxes. Now what I want to do is when I select any item from both the ComboBoxes the Button which is outside the DataGrid should get enabled.
My DataGrid is bind to an ItemSource so does the Comboboxes.
I tries to use MuliDatatriggers but they failed as button is outside the DataGrid so those ComboBoxes will not be available to it.
<DataGrid>
<DataGrid.Columns>
<DataGridTemplateColumn Width="Auto">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox Name="Combo1" ItemsSource="{Binding Lst1,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" DisplayMemberPath="Code1" SelectedValue="{Binding CodeID1,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">
<ComboBox Name="Combo2" ItemsSource="{Binding Lst2,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" DisplayMemberPath="Code2" SelectedValue="{Binding CodeID2,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<Button Name="Add" IsEnabled="{Binding IsAddEnabled,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
There is a lot of answers to this question already posted.
For example: Enable text box when combobox item is selected
The better way for you is to apply MVVM to your application.
I agree with #MikroDel with the MVVM that the only way to work right in wpf.. I do somthing like this but not with two cmbs and not on datagrid but that not need to be different at all because in each combo you set the selected index to your property on the viewModel and the same for button.
in this exemple i use RelayCommand you can read hare about using it, but that not this q subject.
In addition I use a convertor 'cause like the button be enabled also if selected index = 0 so it implementd very simply
namespace MCSearchMVVM
{
class MCBindButtonToComboBox : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null)
return false;
return true;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{ throw new NotImplementedException(); }
}
}
Now to the real stuff ;)
Little advice before that is I like allways put the view(.xaml file) and the vm(.cs file) on the same folder, that why i find this example very fast lol
First we begon with the view:
<UserControl x:Class="MCSearchMVVM.AddFilePage"
...
xmlns:local="clr-namespace:MCSearchMVVM"
...>
<UserControl.Resources>
<local:MCBindButtonToComboBox x:Key="enableCon"/>
</UserControl.Resources>
<Grid>
<Grid.ColumnDefinitions>
...
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
...
</Grid.RowDefinitions>
<Grid.Background>
...
</Grid.Background>
<Button Content="Browse.."
...
Command="{Binding BrowseCommand}"
IsEnabled="{Binding FileKindIndexSelected,
Converter={StaticResource enableCon}}"
.../>
<ComboBox ... SelectedIndex="{Binding FileKindIndexSelected, Mode=TwoWay}" ... >
...
</ComboBox>
...
</Grid>
Now the ViewModel :
public class AddFileViewModel : ObservableObject, IPageViewModel
{
...
private int _fileKindIndexSelected;
public int FileKindIndexSelected
{
get { return _fileKindIndexSelected; }
set { SetField(ref _fileKindIndexSelected, value, "FileKindIndexSelected");}
}
...
}
And the SetField func
public abstract class ObservableObject : INotifyPropertyChanged
{
[Conditional("DEBUG")]
[DebuggerStepThrough]
public virtual void VerifyPropertyName(string propertyName)
{
if (TypeDescriptor.GetProperties(this)[propertyName] == null)
{
string msg = "Invalid property name: " + propertyName;
if (this.ThrowOnInvalidPropertyName)
throw new Exception(msg);
else
Debug.Fail(msg);
}
}
protected virtual bool ThrowOnInvalidPropertyName { get; private set; }
#region INotifyPropertyChanged
public virtual void RaisePropertyChanged(string propertyName)
{
this.VerifyPropertyName(propertyName);
OnPropertyChanged(propertyName);
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
protected bool SetField<T>(ref T field, T value, string propertyName)
{
if (EqualityComparer<T>.Default.Equals(field, value))
return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
#endregion // INotifyPropertyChanged
}
}
I Hope that direction was helpfull..
And sorry for my bad English =))