Button Enablity WPF - c#

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 =))

Related

How to update visibility of an UI element in a DataTemplate in GridView

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.

WPF: two way data binding is not working

I found some silimar questions, but these are not exactly what i need.
I want to bound stackpanel "IsEnabled" value to bool "!IsIterrupted" value of my Items. Here is my XAML file:
<ListView ItemsSource="{Binding Path=Items}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel IsEnabled="{Binding !IsInterrupted, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
<Button Command="{Binding Path=StopThreadCommand, Source={StaticResource viewModel}}" CommandParameter="{Binding Id}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
This is how items looks like:
public class ThreadDecorator : BaseThread , INotifyPropertyChanged
{
...
public event PropertyChangedEventHandler PropertyChanged;
private bool _is_interrupted;
public bool IsInterrupted
{
get { return _is_interrupted; }
set
{
_is_interrupted = value;
OnPropertyChanged("IsInterrupted");
}
}
protected virtual void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
...
}
And my ViewModel:
public class ThreadsViewModel : DependencyObject
{
private ThreadsModel _model;
public ThreadsModel Model
{
get { return _model; }
set
{
_model = value;
}
}
public ICollectionView Items
{
get { return (ICollectionView)GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
public static readonly DependencyProperty ItemsProperty =
DependencyProperty.Register("Items", typeof(ICollectionView), typeof(ThreadsViewModel), new PropertyMetadata(null));
public StopThreadCommand StopThreadCommand { get; set; }
public ThreadsViewModel()
{
this.Model = new ThreadsModel();
Items = CollectionViewSource.GetDefaultView(Model.Threads);
this.StopThreadCommand = new StopThreadCommand(this);
}
public void InterruptThread(int id)
{
_model.InterruptThread(id);
}
}
StopThreadCommand:
public class StopThreadCommand : ICommand
{
public ThreadsViewModel ViewModel {get; set;}
public StopThreadCommand(ThreadsViewModel viewModel)
{
this.ViewModel = viewModel;
}
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
this.ViewModel.InterruptThread((int)parameter);
}
}
When I am clicking on Stop button, IsInterrupted value is changing from false to true, and stackpanel have to become disabled, but UI does not update. Help please!
The default property of Binding is Path, which is a path to a property/sub-property of the DataContext. It's not an arbitrary C# expression. So you're setting Binding.Path to "!IsInterrupted". !IsInterrupted won't evaluate to the boolean inverse of IsInterrupted; it won't evaluate to anything. It'll get you this in the debug output stream:
System.Windows.Data Error: 40 : BindingExpression path error: '!IsInterrupted' property not found on 'object' 'ThreadDecorator' blah blah blah
<StackPanel
IsEnabled="{Binding !IsInterrupted, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
One way to do this is to write a boolean-inverse value converter (stolen verbatim from Chris Nicol's answer at the other end of that link):
[ValueConversion(typeof(bool), typeof(bool))]
public class InverseBooleanConverter: IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
if (targetType != typeof(bool))
throw new InvalidOperationException("The target must be a boolean");
return !(bool)value;
}
public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
#endregion
}
Usage:
<UserControl.Resources>
<local:InverseBooleanConverter x:Key="InverseBooleanConverter" />
</UserControl.Resources>
<!-- stuff etc. -->
IsEnabled="{Binding Path=IsReadOnly, Converter={StaticResource InverseBooleanConverter}}"
You could also write a Style with a DataTrigger that sets IsEnabled to False if IsInterrupted is true.

Two Data binding in same control WPF

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

Hide list view until button is pressed

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.

wpf checkbox list not updating

I have the following ui items - one checkbox list and one checkbox with toggle all checkboxes in that list -
<DockPanel>
<CheckBox
Name="SelectCheckboxes"
Command="{Binding ToggleCheckBoxes}"
Content="Whatever"/>
</DockPanel>
<DockPanel>
<ListBox Name="MyListBox"
ItemsSource="{Binding Path=MyProperty, Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Name="MyCheckBox"
Content="{Binding myvalue}"
Tag="{Binding mycode}"
IsChecked="{Binding Path=isChecked, Mode=TwoWay}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DockPanel>
And here is the MyProperty property -
public ObservableCollection<SomeEntity> MyProperty
{
get { return _someEntities; }
set
{
if (value == _someEntities)
return;
_someEntities = value;
base.OnPropertyChanged("MyProperty");
}
}
And here is a command ToggleCheckBoxes -
public ICommand ToggleCheckBoxes
{
get
{
if (_toggleCheckBoxesCommand == null)
{
_toggleCheckBoxesCommand = new RelayCommand(
param => this.toggleCheckBoxes()
);
}
return _toggleCheckBoxesCommand;
}
}
void toggleCheckBoxes()
{
foreach (var i in MyProperty)
{
if (i.isChecked)
i.isChecked = false;
else
i.isChecked = true;
}
}
When I click on the checkbox to toggle the checkboxes, I can look at the property in the code and see that the isChecked property is changed, but the ListBox does not update to reflect that all items are checked/unchecked.
Does anyone see anything that I am missing that might cause the ListBox not to update?
Thnaks for any thoughts.
Make sure that your isChecked member is actually a property and that SomeEntity implements INotifyPropertyChanged. Something like:
public class SomeEntity : INotifyPropertyChanged {
private bool _isChecked;
public bool isChecked
{
get { return _isChecked; }
set
{
if (value == _isChecked)
return;
_isChecked= value;
this.NotifyPropertyChanged("isChecked");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}

Categories

Resources