Disable validation for a button in IDataErrorInfo - wpf

I have two buttons "Search" and "Clear" on my View for which I have two commands on my view model. I have implemented IDataErrorInfo on my ViewModel and validated the input fields. How can I disable validation for the Clear button?
<TextBox Text="{Binding SearchText, Mode=TwoWay, ValidatesOnDataErrors=True, NotifyOnValidationError=True, ValidatesOnExceptions=True}"
Validation.ErrorTemplate="{StaticResource ErrorTemplate}"/>
<Button Content="Search" Command="{Binding SearchCommand}" />
<Button Content="Clear" Command="{Binding ClearCommand}" />

I am assumed you want to enable / disable the clear button based on validation in the search textbox. I have used the MvvmLight Relaycommand for commanding from the latest MVVMLight using the namespace using GalaSoft.MvvmLight.CommandWpf; refer the code below.
<Window x:Class="DataTemplateSelector_Learning.Window3"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window3" Height="300" Width="300">
<Grid>
<StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Search Text"/>
<TextBox Width="100" Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged,Mode=TwoWay,
ValidatesOnDataErrors=True, NotifyOnValidationError=True, ValidatesOnExceptions=True}" />
</StackPanel>
<Button Content="Search" Command="{Binding SearchCommand}" />
<Button Content="Clear" Command="{Binding ClearCommand}" />
</StackPanel>
</Grid>
public partial class Window3 : Window
{
public Window3()
{
InitializeComponent();
this.DataContext = new ViewModel();
}
}
class ViewModel:INotifyPropertyChanged,IDataErrorInfo
{
private string searchText;
private bool enableClear;
public string SearchText
{
get { return searchText; }
set { searchText = value; Notify("SearchText"); }
}
public ICommand SearchCommand { get; set; }
public ICommand ClearCommand { get; set; }
public ViewModel()
{
ClearCommand = new RelayCommand(OnClear, CanClear);
}
private bool CanClear()
{
return enableClear;
}
private void OnClear()
{
}
public event PropertyChangedEventHandler PropertyChanged;
private void Notify(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
public string Error
{
get { return String.Empty; }
}
public string this[string columnName]
{
get
{
String errorMessage = String.Empty;
if (!string.IsNullOrEmpty(SearchText))
{
if (SearchText.Length > 0)
{
enableClear = true;
}
else
{
enableClear = false;
}
}
return errorMessage;
}
}
}

Related

Command not executing when clicking a button

How should the collection be bound to Listview and have the button execute the command to add a single user to the collection.
View Model Code:
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
public class UserViewModel : ViewModelBase
{
private ObservableCollection<User> _UsersList;
private User _user;
public UserViewModel()
{
_user = new User();
_UsersList = new ObservableCollection<User>();
_UsersList.CollectionChanged += _UsersList_CollectionChanged;
}
private void _UsersList_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
}
public ObservableCollection<User> Users
{
get { return _UsersList; }
set
{
_UsersList = value;
OnPropertyChanged("Users");
}
}
public User User
{
get
{
return _user;
}
set
{
_user = value;
OnPropertyChanged("User");
}
}
private ICommand mUpdater;
public ICommand UpdateCommand
{
get
{
if (mUpdater == null)
mUpdater = new Updater(this);
return mUpdater;
}
//set
//{
// mUpdater = value;
//}
}
private void Submit()
{
Users.Add(User);
User = new User();
}
private class Updater : ICommand
{
#region ICommand Members
UserViewModel _obj;
public Updater(UserViewModel obj)
{
_obj = obj;
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
_obj.Submit();
}
}
View Xaml:
<ListView Name="UserGrid"
Grid.Row="1"
Margin="4,178,12,13"
ItemsSource="{Binding Users}" >
<ListView.View>
<GridView x:Name="grdTest">
<GridViewColumn Header="UserId"
DisplayMemberBinding="{Binding UserId}"
Width="50"/>
</GridView>
</ListView.View>
</ListView>
<TextBlock Grid.Row="0"
Grid.Column="0"
Text="Name"
HorizontalAlignment="Center"/>
<TextBox Grid.Row="0"
Grid.Column="1"
Width="100"
HorizontalAlignment="Center"
Text="{Binding User.UserId, Mode=TwoWay}"/>
<Button Content="Update"
Grid.Row="1"
Height="23"
HorizontalAlignment="Left"
Margin="310,40,0,0"
Name="btnUpdate"
VerticalAlignment="Top"
Width="141"
Command="{Binding Path=UpdateCommad}" />
The error is in the binding of the button to the command:
Command="{Binding Path=UpdateCommad}" />
It should be:
Command="{Binding Path=UpdateCommand}" />
To find errors like this always read the Debug Output. In this case it quickly showed:
System.Windows.Data Error: 40 : BindingExpression path error:
'UpdateCommad' property not found on 'object' ''UserViewModel'
To prevent this from happening at all you could use Xaml code completion by setting the design-time DataContext type:
<Window x:Class="TestWpfApplication.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TestWpfApplication"
mc:Ignorable="d"
Title="MainWindow"
Height="350"
Width="525"
d:DataContext="{d:DesignInstance Type=local:UserViewModel}">
After setting this you will have code-completion in Xaml: just start typing the binding path and it will offer valid options.

How to update the textblock through NewFile menu click command

I am new to the MVVM pattern. I am facing problem in updating the textblock.I have one textblock and menu button.
//View.xaml for textblock
<Grid DataContext="{Binding Source={StaticResource MenuHandler}}" Height="23" HorizontalAlignment="Stretch" VerticalAlignment="Top" Grid.Row="2" Grid.ColumnSpan="3" Margin="5,0,5,0" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="200" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Path=StatusText, UpdateSourceTrigger=PropertyChanged}" Name="StatusText" Grid.Column="0" HorizontalAlignment="Left" VerticalAlignment="Stretch" Width="100" IsEnabled="True" Height="23"/>
</Grid>
//View.xaml for Menu
<Menu DataContext="{Binding Source={StaticResource MenuHandler}}" Height="25" HorizontalAlignment="Stretch" Name="menu1" VerticalAlignment="Top" Visibility="Visible" Background="AliceBlue">
<MenuItem Header="_New" Command="{Binding NewFileCommand}" Name="NewFile" >
<MenuItem.Icon>
<Image Source="/WhitelistBlacklistEditor;component/Images/NewFile.png" Width="25"/>
</MenuItem.Icon>
</MenuItem>
</Menu >
In view model i am using MenuHandler class
//MenuHandler.cs
public string StatusText
{
get { return _StatusText; }
set
{
_StatusText = value;
RaisePropertyChangedEvent(StatusText);
}
}
public ICommand NewFileCommand
{
get { return new DelegateCommand(NewFile_Click); }
}
public void NewFile_Click()
{
StatusText = "checking";
}
If i update the StatusText in other than NewFile_Click() method, the value is updating in the textblock but if i do the same in NewFile_Click() through ICommand it is not updating.
I am also inheritted the MenuHandler class by INotifyPropertyChanged and i checked by adding twoway mode in xaml.
public class DelegateCommand : ICommand
{
private readonly Predicate _canExecute;
private readonly Action _execute;
public event Event Handler CanExecuteChanged;
public Delegate Command(Action<object> execute)
: this(execute, null)
{
}
public Delegate Command(Action<object> execute,
Predicate<object> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
public override bool CanExecute(object parameter)
{
if (_canExecute == null)
{
return true;
}
return _canExecute(parameter);
}
public override void Execute(object parameter)
{
_execute(parameter);
}
public void RaiseCanExecuteChanged()
{
if( CanExecuteChanged != null )
{
CanExecuteChanged(this, EventArgs.Empty);
}
}
}
private DelegateCommand _NewFileCommand;
public ICommand NewFileCommand
{
get
{
if (adcom == null)
adcom = new DelegateCommand(CommandExecuter, CommandExecute);
return adcom;
}
}
private bool CommandExecuter(object obj)
{
return true;
}
private void CommandExecute(object obj)
{
Statuscheck="";
}

Checkbox is not checked in DevExpress GridControl using wpf

I am trying to work on DevExpress GridControl Checkbox column but problem is that when I binding the checkbox value in XAML code dynamically its not work perfectlly
below I provide you my demo project code :-
XAML Code:-
<dxg:GridControl AutoPopulateColumns="True" HorizontalAlignment="Left" Margin="0,40,0,0" Name="gridControl1" VerticalAlignment="Top" Height="318" Width="503">
<dxg:GridControl.View>
<dxg:TableView Name="tableView1" ShowTotalSummary="True" />
</dxg:GridControl.View>
<dxg:GridControl.Columns>
<dxg:GridColumn DisplayMemberBinding="{Binding Path=EvenOdd}" Header="Even/Odd" />
<dxg:GridColumn Name="PickColumn" Header="Pick" Width="30"
AllowColumnFiltering="False" AllowSorting="False">
<dxg:GridColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Path=IsValid}"
HorizontalAlignment="Center" VerticalAlignment="Center" >
</CheckBox>
</DataTemplate>
</dxg:GridColumn.CellTemplate>
</dxg:GridColumn>
</dxg:GridControl.Columns>
</dxg:GridControl>
my cs file code:-
public class ss
{
public bool IsValid { get; set; }
public string EvenOdd { get; set; }
}
Code Behind:
public List<ss> sList = new List<ss>();
private void Window_Loaded(object sender, RoutedEventArgs e)
{
for (int i = 0; i < 10; i++)
{
if (i % 2 == 0)
{
sList.Add(new ss { IsValid = true, EvenOdd = "Even" });
}
else
{
sList.Add(new ss { IsValid = false, EvenOdd = "Odd" });
}
}
gridControl1.ItemsSource = sList;
}
Adding on to HighCore's answer. If you would like to edit the data in your grid.
See ColumnBase.CellTemplate Property:
To enable data editing, use an editor shipped with the DevExpress Data Editors Library for WPF. The editor's Name must be set to
'PART_Editor'.
Standard controls can be used in CellTemplate only for display purposes. Data editing is not allowed.
Then,
xmlns:dxe="http://schemas.devexpress.com/winfx/2008/xaml/editors"
<dxg:GridColumn Name="PickColumn"
Header="Pick"
Width="30"
AllowColumnFiltering="False"
AllowSorting="False">
<dxg:GridColumn.CellTemplate>
<DataTemplate>
<dxe:CheckEdit x:Name="PART_Editor"
EditValue="{Binding Path=Data.IsValid, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</dxg:GridColumn.CellTemplate>
</dxg:GridColumn>
You still need to use HighCore's implementation of INotifyPropertyChanged.
First of all you need to correct the binding inside the CellTemplate:
<CheckBox IsChecked="{Binding Path=RowData.Row.IsValid}"/>
Second, your data items should implement INotifyPropertyChanged:
public class ss:INotifyPropertyChanged
{
private bool _isValid;
private string _evenOdd;
public bool IsValid
{
get { return _isValid; }
set
{
_isValid = value;
OnPropertyChanged("IsValid");
}
}
public string EvenOdd
{
get { return _evenOdd; }
set
{
_evenOdd = value;
OnPropertyChanged("EvenOdd");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
xmlns:dxgcore="http://schemas.devexpress.com/winfx/2008/xaml/grid"
<dxgcore:GridColumn Width="20"
AllowEditing="True"
Binding="{Binding Path=IsChecked, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Header="R"
Visible="{Binding CheckboxSelection}"
VisibleIndex="6">
<dxgcore:GridColumn.CellTemplate>
<DataTemplate>
<dxe:CheckEdit HorizontalAlignment="Center"
VerticalAlignment="Center"
Command="{Binding
Path=View.DataContext.IsCheckedCommand}"
CommandParameter="{Binding RowData.Row}"
IsChecked="{Binding RowData.Row.IsChecked, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding Path=View.DataContext.IsCheckBoxEnabled, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</dxgcore:GridColumn.CellTemplate>
</dxgcore:GridColumn>
And Notifychanged property
private bool _isChecked;
public bool IsChecked
{
get { return _isChecked; }
set
{
_isChecked = value;
RaisePropertyChanged("IsChecked");
}
}

WPF Data Templates and Buttons

Note: I am using Visual Studio 2012
I have the following DataTemplate (note: there is a TextBlock and Button)
<DataTemplate DataType="{x:Type ctlDefs:myButton}">
<StackPanel>
<TextBlock Text="{Binding LabelText}" Margin="10,0,10,0"/>
<Button Content="{Binding LabelText}" Margin="0,0,10,0"
Width="{Binding ButtonWidth}"
Height="35"
Command="{Binding ButtonCommand}"
Visibility="Visible"
/>
</StackPanel>
</DataTemplate>
My screen is dynamically adding controls of myButton to an Items Control
<ItemsControl ItemsSource="{Binding CommandButtons, Mode=OneWay}"
FlowDirection="LeftToRight"
DockPanel.Dock="Right"
>
The first time I render the view, the Textblock shows up for each button, but the Button itself does not. If I hide the view and then show it again, the button will appear sometimes (if the CommandButtons list does not change, if it changes, it never shows).
After rendering the view, I looked in the Output window and no binding errors.
What am I missing.
I've knocked up a quick application to work with your sample code and didn't seem to have any issues with the view. Below is what it looks like when run.
I've included my code below in case it helps. Note: I didn't add a real ICommand object for brevity and I accidentally named the MyButton class with capital M instead of small m like your example
ViewModelBase.cs
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
var handler = this.PropertyChanged;
if (handler != null)
{
handler(this, e);
}
}
}
public class MainWindowViewModel : ViewModelBase
{
public MainWindowViewModel()
{
this.CommandButtons = new ObservableCollection<MyButton>();
}
public ObservableCollection<MyButton> CommandButtons { get; private set; }
}
App.xaml.cs
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var mainvm = new MainWindowViewModel();
var window = new MainWindow
{
DataContext = mainvm
};
window.Show();
mainvm.CommandButtons.Add(new MyButton { ButtonWidth = 50, LabelText = "Button 1" });
mainvm.CommandButtons.Add(new MyButton { ButtonWidth = 50, LabelText = "Button 2" });
mainvm.CommandButtons.Add(new MyButton { ButtonWidth = 50, LabelText = "Button 3" });
}
}
MainWindow.xaml
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ctlDefs="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate DataType="{x:Type ctlDefs:MyButton}">
<StackPanel>
<TextBlock Text="{Binding LabelText}" Margin="10,0,10,0"/>
<Button Content="{Binding LabelText}" Margin="0,0,10,0"
Width="{Binding ButtonWidth}"
Height="35"
Command="{Binding ButtonCommand}"
Visibility="Visible"
/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<ItemsControl ItemsSource="{Binding CommandButtons, Mode=OneWay}"
FlowDirection="LeftToRight"
DockPanel.Dock="Right"
/>
</Grid>
MyButton.cs
public class MyButton : ViewModelBase
{
private string _labelText;
public string LabelText
{
get { return this._labelText; }
set { this._labelText = value; this.OnPropertyChanged("LabelText"); }
}
private double _buttonWidth;
public double ButtonWidth
{
get { return _buttonWidth; }
set { _buttonWidth = value; this.OnPropertyChanged("ButtonWidth"); }
}
private ICommand _buttonCommand;
public ICommand ButtonCommand
{
get { return _buttonCommand; }
set { _buttonCommand = value; this.OnPropertyChanged("ButtonCommand"); }
}
}

Data Binding : Child accessing AncestorType property

Bellow is the code behind and the Xaml for a demo app to review databing and wpf.
The problem is binding Store.ImagePath property to the person node is not working. That is the image is not showing.
<Image Source="{Binding Path=Store.ImagePath, RelativeSource={RelativeSource AncestorType={x:Type local:Store}}}" />
Here is the code-behind
namespace TreeViewDemo
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Customers customers = new Customers();
customers.Users = new List<Person>
{
new Person { Name = "John"},
new Person { Name = "Adam"},
new Person { Name = "Smith"}
};
Store store = new Store();
store.AllCustomers.Add(customers);
this.DataContext = store;
}
}
public class Store : INotifyPropertyChanged
{
string imagePath = "imageone.png";
public Store()
{
AllCustomers = new ObservableCollection<Customers>();
}
public string StoreName
{
get
{
return "ABC Store";
}
}
public ObservableCollection<Customers> AllCustomers
{
get;
set;
}
public string ImagePath
{
get
{
return imagePath;
}
set
{
if (value == imagePath) return;
imagePath = value;
this.OnPropertyChanged("ImagePath");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public class Customers
{
public string Label
{
get
{
return string.Format("People({0})", Users.Count());
}
}
public List<Person> Users
{
get;
set;
}
}
public class Person : INotifyPropertyChanged
{
public string Name
{
get;
set;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
and here is the Xaml.
<Window x:Class="TreeViewDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TreeViewDemo"
Title="MainWindow" Height="350" Width="525">
<Window.Resources >
<DataTemplate DataType="{x:Type local:Person}" x:Key="personKey" >
<StackPanel Orientation="Horizontal" >
<Image Source="{Binding Path=Store.ImagePath, RelativeSource={RelativeSource AncestorType={x:Type local:Store}}}" />
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
<HierarchicalDataTemplate x:Key="customerKey" ItemsSource="{Binding Users}" ItemTemplate="{StaticResource personKey }" >
<TextBlock Text="{Binding Label}" FontWeight="Bold"/>
</HierarchicalDataTemplate>
</Window.Resources>
<Grid>
<Canvas>
<Button HorizontalAlignment="Left" DockPanel.Dock="Top" Height="29" Width="112" Canvas.Left="123" Canvas.Top="5">Image one</Button> <Button HorizontalAlignment="Left" VerticalAlignment="Top" DockPanel.Dock="Top" Height="28" Width="119" Canvas.Left="249" Canvas.Top="7">Image two</Button>
<TreeView HorizontalAlignment="Stretch" Name="treeView1" VerticalAlignment="Stretch"
ItemsSource="{Binding .}" Height="260" Width="363" Canvas.Left="81" Canvas.Top="45">
<TreeViewItem ItemsSource="{Binding AllCustomers}" ItemTemplate="{StaticResource customerKey}" Header="{Binding StoreName}"></TreeViewItem>
</TreeView>
</Canvas>
</Grid>
</Window>
All files are in the same directory.
Thanks
A relative source is used to look up an object in the visual tree. You're asking it to find the nearest Store in the visual tree. Since a Store cannot even be in the visual tree, the lookup will fail and yield null. What you actually want is the DataContext of the root Window, since that is where your Store is held:
<Image Source="{Binding DataContext.ImagePath, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />

Resources