I'm trying to bind a textbox (named Labour) and textblock (which shows time) with listview. Listview should show a names of labours and times that they're takes after cliking button "Add item". I've already tried to use observablecollection but I'm doing something wrong. Data in ListView should be separated in two columns.
How can i do that ?
XAML:
<Window x:Class="LabourTimer.View.LabourMainWindow"
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:LabourTimer.View"
mc:Ignorable="d"
xmlns:viewmodel="clr-namespace:LabourTimer.ViewModel"
Title="Labour timer" Height="600" Width="515">
<Window.Resources>
<viewmodel:LabourViewModel x:Key="viewModel"/>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition/>
</Grid.RowDefinitions>
<StackPanel>
<Label Content="Timer:" Grid.Row="0"/>
<TextBlock Text="{Binding CurrentTime}" Grid.Row="0"/>
<Label Content="Labour name:"/>
<TextBox x:Name="Labour" Text="{Binding labourName, Mode=TwoWay}"/>
</StackPanel>
<StackPanel Grid.Column="1">
<Button Content="Start/Stop" Command="{Binding StartStopCommand}"/>
<Button Content="Reset" Command="{Binding ResetCommand}"/>
<Button Content="Add to list" Command="{Binding AddCommand}"/>
<Button Content="Remove from list"/>
</StackPanel>
<ListView x:Name="listView" Height="Auto" Margin="0,5" Grid.Row="1" VerticalAlignment="Top" Grid.ColumnSpan="2" MinWidth="300">
<ListView.View>
<GridView>
<GridViewColumn Header="Labour name" Width="400" />
<GridViewColumn Header="Time in seconds" Width="100"/>
</GridView>
</ListView.View>
</ListView>
</Grid>
</Window>
ViewModel:
namespace LabourTimer.ViewModel
{
public class LabourViewModel : INotifyPropertyChanged
{
private DispatcherTimer _timer;
private string _currentTime;
private int _seconds;
private bool _running;
public ICommand StartStopCommand { get; set; }
public ICommand ResetCommand { get; set; }
public ICommand AddCommand { get; set; }
public string CurrentTime
{
get { return _currentTime; }
set { _currentTime = value;
OnPropetyChanged("CurrentTime");
}
}
public LabourViewModel()
{
_timer = new DispatcherTimer();
_timer.Interval = TimeSpan.FromSeconds(1);
_timer.Tick += new EventHandler(TimerTick);
_running = false;
CurrentTime = "0";
LoadCommands();
}
private void LoadCommands()
{
StartStopCommand = new CustomCommand(StartStopTimer, CanStartStopTimer);
ResetCommand = new CustomCommand(ResetTimer, CanResetTimer);
AddCommand = new CustomCommand(AddLabour, CanAddLabour);
}
private void AddLabour(object obj)
{
}
private bool CanAddLabour(object obj)
{
return true;
}
private bool CanResetTimer(object obj)
{
if (CurrentTime != "0")
return true;
else
return false;
}
private void ResetTimer(object obj)
{
_timer.Stop();
CurrentTime = "0";
_seconds = 0;
}
private void StartStopTimer(object obj)
{
if (_running == false)
{
_timer.Start();
_running = true;
}
else
{
_timer.Stop();
_running = false;
}
}
private bool CanStartStopTimer(object obj)
{
return true;
}
private void TimerTick(object send, EventArgs e)
{
_seconds++;
CurrentTime = _seconds.ToString();
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropetyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
CustomCommand:
public class CustomCommand : ICommand
{
private Action<object> execute;
private Predicate<object> canExecute;
public CustomCommand(Action<object> execute, Predicate<object> canExecute)
{
this.execute = execute;
this.canExecute = canExecute;
}
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
}
remove
{
CommandManager.RequerySuggested -= value;
}
}
public bool CanExecute(object parameter)
{
bool b = canExecute == null ? true : canExecute(parameter);
return b;
}
public void Execute(object parameter)
{
execute(parameter);
}
}
}
MainWindow:
public partial class LabourMainWindow : Window
{
public LabourMainWindow()
{
InitializeComponent();
DataContext = new LabourViewModel();
}
Related
I have two text box and want to copy first textbox value to another textbox whenever I click on Button and this should done by using Commands in WPF.
This is my scenario :
First textbox binds the value from Person class.
Button shows simple MsgBox which verifies that Command executed properly.
Well here, I want to pass first textbox value to 2nd textbox (using Command) ?
XML File:
<Window x:Class="PrismDemo.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:PrismDemo.ViewModels"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<vm:Person x:Name="vmmmm1" />
</Window.DataContext>
<Grid>
<TextBox x:Name="fName" Grid.Row="1" Height="30" Width="100" Text="{Binding Path=FirstName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<Button Name="Submit" Grid.Row="2" Height="30" Width="100" Content="Submit Me" Command="{Binding submitCommand}" CommandParameter="{Binding Text, ElementName=fName}"/>
<TextBox x:Name="display" Grid.Row="3" Height="30" Width="100" Text="{}" />
Person class (ViewModel):
public class Person:INotifyPropertyChanged
{
private string _firstName;
private string _copyName;
public ICommand submitCommand {get;set;}
public Person()
{
_firstName = "Ronaldo";
submitCommand = new RelayCommand(MyMethod, canExecuteMethod);
}
public string FirstName
{
get
{
return _firstName;
}
set
{
_firstName = value;
OnPropertyUpdated(FirstName);
//OnPropertyUpdated(CopyName);
}
}
public string CopyName
{
get
{
return _copyName;
}
set
{
OnPropertyUpdated(CopyName);
}
}
private void OnPropertyUpdated(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
private bool canExecuteMethod(object parameter)
{
return true;
}
private void MyMethod(object parameter)
{
MessageBox.Show("Welcome to Command Demo...");
//if (parameter == null) return;
//_copyName = parameter.ToString();
this._copyName = _firstName;
}
public event PropertyChangedEventHandler PropertyChanged;
}
Any help will be appreciated.
Thank you !!
You were almost right ....Its working at my place properly , Just make following changes in you code
Just remove command parameter... we dont need it and Bind the copied string.
<TextBox Grid.Row="1" Height="30" Width="100" Text="{Binding FirstName}" />
<Button Grid.Row="2" Height="30" Width="100" Content="Submit Me" Command="{Binding submitCommand}"/>
TextBox Grid.Row="3" Height="30" Width="100" Text="{Binding CopyName}" />
In View model make following changes...
public class Person:INotifyPropertyChanged{
private string _firstName;
private string _copyName=string.Empty;
public Person()
{
_firstName = "Ronaldo";
submitCommand = new RelayCommand(MyMethod, canExecuteMethod);
}
public string FirstName
{
get
{
return _firstName;
}
set
{
_firstName = value;
OnPropertyChanged("FirstName");
}
}
public string CopyName
{
get
{
return _copyName;
}
set
{
if (_copyName != value)
{
_copyName = value;
OnPropertyChanged("CopyName");
}
}
}
public ICommand submitCommand { get; set; }
private void MyMethod(object param)
{
MessageBox.Show("Welcome to Command Demo...");
CopyName = FirstName;
}
private bool canExecuteMethod(object parameter)
{
return true;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(params string[] propertyNames)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
foreach (string propertyName in propertyNames) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
handler(this, new PropertyChangedEventArgs("HasError"));
}
}
}
You don't need CommandParameter here.
<Button Name="Submit" Grid.Row="2" Height="30" Width="100" Content="Submit Me" Command="{Binding submitCommand}" />
Add the Display property:
public string Display
{
get
{
return _display;
}
set
{
_display = value;
OnPropertyUpdated(Display);
}
}
Fix the binding in the second TextBox:
<TextBox x:Name="display" Grid.Row="3" Height="30" Width="100" Text="{Binding Display}" />
Update MyMethod:
private void MyMethod(object parameter)
{
MessageBox.Show("Welcome to Command Demo...");
Display = FirstName;
}
Here is how to copy text from one textBox to another.
this is dataContext behinde MainWindow
public class TestVM : INotifyPropertyChanged
{
public TestVM()
{
CopyCommand = new RelayCommand<string>(OnCopyExecuted);
}
private void OnCopyExecuted(string commandParameter)
{
TextUpdate = commandParameter;
}
private string _textUpdate;
public string TextUpdate
{
get { return _textUpdate; }
set
{
if (_textUpdate != value)
{
_textUpdate = value;
OnPropertyChanged();
}
}
}
public RelayCommand<string> CopyCommand { get; private set; }
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Generic RelayCommand that can take parameters
public class RelayCommand<T> : ICommand
{
private Action<T> _executeMethod;
private Func<T, bool> _canExecuteMethod;
#region RelayCommand ctor
public RelayCommand(Action<T> executeMethod)
{
_executeMethod = executeMethod;
}
public RelayCommand(Action<T> executeMethod, Func<T, bool> canExecuteMethod)
{
_executeMethod = executeMethod;
_canExecuteMethod = canExecuteMethod;
}
#endregion
public void RaiseCanExecuteChanged()
{
CanExecuteChanged(this, EventArgs.Empty);
}
#region ICommand Members
bool ICommand.CanExecute(object parameter)
{
var Tparam = (T)parameter;
if (_canExecuteMethod != null)
return _canExecuteMethod(Tparam);
if (_executeMethod != null)
return true;
return false;
}
void ICommand.Execute(object parameter)
{
if (_executeMethod != null)
_executeMethod((T)parameter);
}
public event EventHandler CanExecuteChanged = delegate { };
#endregion
}
and MainWindow xaml just to show purpose
<Window.DataContext>
<local:TestVM />
</Window.DataContext>
<Grid>
<TextBox x:Name="txt1"
Height="35"
Width="150"
Margin="49,62,318,224" />
<TextBox Text="{Binding TextUpdate}"
Height="35"
Width="150"
Margin="313,62,54,226" />
<Button Command="{Binding CopyCommand}"
CommandParameter="{Binding ElementName=txt1,Path=Text}"
Content="Copy"
Grid.Row="0"
Margin="208,157,198,132" />
</Grid>
It's working. Now you can implement it as it fits your needs.
I have got a View who's DataContext is set to an Employee.
Further, the view uses a BindingGroup and Validation Rules.
At last the view has got 2 Buttons: Save and Cancel
Save: Validate the users input and in case of success, save the changes.
Cancel: Rollback the user input and restore the original values.
Until this point it works fine.
Now the last requirement and the problem:
For a better User Experience i would like to enable the save Button when the user begins to change data.
To achieve this, I bind the IsDirty Property of the BindingGroup to the Enabled Property of the Button.
Unfortunately it doesn't work. The binding seems to be correct, but the user interface does not recognize the change of IsDirty.
Who can i solve this problem?
My Model:
public class EmployeeModel:ModelBase
{
private int _nr;
private string _firstname;
private string _lastname;
public int Nr
{
get
{
return _nr;
}
set
{
_nr = value;
OnChanged(nameof(Nr));
}
}
public string Firstname
{
get
{
return _firstname;
}
set
{
_firstname = value;
OnChanged(nameof(Firstname));
}
}
public string Lastname
{
get
{
return _lastname;
}
set
{
_lastname = value;
OnChanged(nameof(Lastname));
}
}
}
ModelBase:
public class ModelBase:INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnChanged(string propertyname)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyname));
}
}
ValidationRule:
public class EmployeeValidationRule:ValidationRule
{
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
BindingGroup bindingGroup = (BindingGroup)value;
if (bindingGroup.Items.Count == 2)
{
EmployeeModel employee = (EmployeeModel)bindingGroup.Items[1];
string firstname = (string)bindingGroup.GetValue(employee, "Firstname");
string lastname = (string)bindingGroup.GetValue(employee, "Lastname");
if (firstname.Length == 0)
return new ValidationResult(false, "Firstname can not be empty.");
if (lastname.Length == 0)
return new ValidationResult(false, "Lastname can not be empty.");
}
return ValidationResult.ValidResult;
}
}
My ViewModel:
public class EmployeeViewModel
{
private EmployeeModel _employeeModel;
public EmployeeModel Employee
{
get
{
return _employeeModel;
}
set
{
_employeeModel = value;
}
}
public EmployeeViewModel()
{
LoadData();
}
private void LoadData()
{
//Employee = (from e in _context.Employee
// where e.Nr == 158
// select e).FirstOrDefault();
Employee = new EmployeeModel() { Firstname = "Billy", Lastname = "Wilder" };
}
public void Save()
{
//_context.SaveChanges();
}
}
At last the View:
<Window x:Class="WpfApplication3_Validation.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:WpfApplication3_Validation"
xmlns:vm="clr-namespace:WpfApplication3_Validation.ViewModel"
xmlns:vr="clr-namespace:WpfApplication3_Validation.ValidationRules"
mc:Ignorable="d"
Title="Employee" Height="250" Width="525"
Validation.ValidationAdornerSite="{Binding ElementName=lbErrors}" Loaded="Window_Loaded">
<Window.DataContext>
<vm:EmployeeViewModel/>
</Window.DataContext>
<Window.BindingGroup>
<BindingGroup x:Name="MyBindingGroup">
<BindingGroup.ValidationRules>
<vr:EmployeeValidationRule/>
</BindingGroup.ValidationRules>
</BindingGroup>
</Window.BindingGroup>
<Grid x:Name="gridMain">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label Content="Nr:"/>
<TextBlock Grid.Column="1" Text="{Binding Employee.Nr}"/>
<Label Grid.Row="1" Content="Vorname:" Target="{Binding ElementName=tbFirstname}"/>
<TextBox Grid.Row="1" Grid.Column="1" x:Name="tbFirstname" Text="{Binding Employee.Firstname}"/>
<Label Grid.Row="2" Content="Nachname:" Target="{Binding ElementName=tbLastname}"/>
<TextBox Grid.Row="2" Grid.Column="1" x:Name="tbLastname" Text="{Binding Employee.Lastname}"/>
<Label Grid.Row="4" Grid.Column="0" x:Name="lbErrors" Content="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.ValidationAdornerSiteFor).(Validation.Errors)[0].ErrorContent}"
Foreground="Red" FontWeight="Bold"/>
<StackPanel Grid.Row="4" Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right">
<TextBlock x:Name="tbIsDirty"/>
<Button x:Name="btn1" Content="IsDirty?" Click="btn1_Click"/>
<Button x:Name="btnSave" Content="Save1" Click="btnSave_Click" />
<Button x:Name="btnSave1" Content="Save2" Click="btnSave_Click" IsEnabled="{Binding ElementName=MyBindingGroup, Path=IsDirty}"/>
<Button x:Name="btnCancel" Content="Cancel" Click="btnCancel_Click"/>
</StackPanel>
</Grid>
Code Behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
this.MyBindingGroup.BeginEdit(); // Not really needed?
}
private void btnSave_Click(object sender, RoutedEventArgs e)
{
if (this.BindingGroup.CommitEdit())
{
EmployeeViewModel vm = (EmployeeViewModel)this.DataContext;
vm.Save();
}
}
private void btnCancel_Click(object sender, RoutedEventArgs e)
{
this.BindingGroup.CancelEdit();
}
private void btn1_Click(object sender, RoutedEventArgs e)
{
tbIsDirty.Text = BindingGroup.IsDirty.ToString();
}
}
Due to the fact that BindingGroup.IsDirty does not Implement INotifyPropertyChanged, it's not a useful source for this type of databinding.
Possible solution:
- Implementing INotifyPropertyChanged in the view
- Creating a own IsDirty in the view, using INotifyPropertyChanged
- Adding an event handler for KeyUp, which sets my IsDirty in case of BindingGroup.IsDirty.
- Binding of Enabled to the new Property
Disadvantage: Need if implementation of INotifyPropertyChanged in the view.
Advantage: It works.
CodeBehind of View:
public partial class MainWindow : Window, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnChanged(string propertyname)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyname));
}
private bool _isDirty;
public bool IsDirty
{
get
{
return _isDirty;
}
set
{
_isDirty = value;
OnChanged(nameof(IsDirty));
}
}
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
this.MyBindingGroup.BeginEdit(); // Not really needed?
gridMain.KeyUp += GridMain_KeyUp;
}
private void GridMain_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)
{
if (this.MyBindingGroup.IsDirty)
{
IsDirty = true;
}
}
private void btnSave_Click(object sender, RoutedEventArgs e)
{
if (this.BindingGroup.CommitEdit())
{
EmployeeViewModel vm = (EmployeeViewModel)this.DataContext;
vm.Save();
IsDirty = false;
}
}
private void btnCancel_Click(object sender, RoutedEventArgs e)
{
this.BindingGroup.CancelEdit();
IsDirty = false;
}
}
Further improvements:
Now i moved IsDirty to my ViewModel, so I don't have to implement INPC in the view. Another advantage is, that in this way, Commands can consume the property and finally i don't have to use databinding for the enabled Property, because i get it over the command.
I want to write a lottery number generator program by using wpf and MVVM structure. I wrote the code below but nothing work. Can you everyone help me?
I have found no mistake and build and debug!
class MainViewModel : ViewModelBase
{
private int duration;
private string text;
private DispatcherTimer timer = null;
public MainViewModel()
{
this.Duration = 1000;
this.Text = "00";
this.StartTimerCommand = new Delegatecommon(this.StartTimer);
this.StopTimerCommand = new Delegatecommon(this.StopTimer);
}
#region Properties
public int Duration
{
get
{
return this.duration;
}
set
{
this.duration = value;
RaisePropertychange("Duration");
}
}
public string Text
{
get
{
return this.text;
}
set
{
this.text = value;
RaisePropertychange("Text");
}
}
public Delegatecommon StartTimerCommand
{
get;
set;
}
public Delegatecommon StopTimerCommand
{
get;
set;
}
#endregion
public void StartTimer()
{
timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromMilliseconds(this.Duration);
timer.Tick += new EventHandler(TimerTick);
timer.Start();
}
public void StopTimer()
{
if (timer != null)
{
timer.Stop();
timer = null;
}
}
private void TimerTick(object send, EventArgs e)
{
Random rnd = new Random((Int32)DateTime.Now.Ticks);
this.Text = rnd.Next(0, 100).ToString();
}
}
In your MainViewModel() add this line: Edited
public MainViewModel()
{
this.Duration = 1000;
this.Text = "00";
timer = new DispatcherTimer();
timer.Interval = this.Duration;
timer.Tick += new EventHandler(TimerTick);
this.StartTimerCommand = new Delegatecommon(this.StartTimer);
this.StopTimerCommand = new Delegatecommon(this.StopTimer);
}
public void StartTimer()
{
timer.Start();
}
public void StopTimer()
{
timer.Stop();
}
Keeping the rest same as before.
After 30 minutes of scrutiny, I figured out 2 words you're missing...
&& PropertyChanged != null in:
class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertychange(string propertyname)
{
if (propertyname != null && PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyname));
}
}
}
Adding that, it worked when I test your code, with my extra modifications that I asked you to make here.
class Delegatecommon : ICommand
{
private Action _execute;
private Func<bool> _canexecute;
public Delegatecommon(Action ac) : this(ac, () => true) { }
public Delegatecommon(Action ac, Func<bool> fu)
{
if (ac == null)
throw new ArgumentNullException();
else if (fu == null)
throw new ArgumentNullException();
_execute = ac;
_canexecute = fu;
}
event EventHandler ICommand.CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
}
remove
{
CommandManager.RequerySuggested -= value;
}
}
public void execute()
{
_execute();
}
public bool canexecute()
{
return _canexecute();
}
bool ICommand.CanExecute(object parameter)
{
return canexecute();
}
void ICommand.Execute(object parameter)
{
execute();
}
}
<Window x:Class="Randm.View.MainView"
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:Randm.ViewModel"
mc:Ignorable="d"
Title="RandomNumber" Height="300" Width="300">
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<TextBlock FontSize="50" VerticalAlignment="Center" HorizontalAlignment="Center" Text="{Binding Path=Text,UpdateSourceTrigger=PropertyChanged}"></TextBlock>
<StackPanel Grid.Row="1">
<Button Width="100" Height="50" Content="Start" FontSize="20" Command="{Binding Path=StartTimerCommand}"></Button>
<Button Width="100" Height="50" Content="Stop" FontSize="20" Command="{Binding Path=StopTimerCommand}"></Button>
</StackPanel>
</Grid>
class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertychange(string propertyname)
{
if(propertyname == null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyname));
}
}
}
I want to disable(close for editing) the 3rd column of my dataGrid(using MVVM-WPF) but want to enable to edit again on click of button outside the Grid.How can I achieve that?
The Challenge I am facing here is how to retrieve dataGrid's property on button click, I am using command pattern out here .This is how my ViewModel looks now, what I am supposed to write in those method to make certain column editable:
public class TicketOverViewViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private TicketDataService ticketDataService;
private ObservableCollection<Ticket> tickets;
public ObservableCollection<Ticket> Tickets
{
get
{
return tickets;
}
set
{
tickets = value;
RaisePropertyChanged("Tickets");
}
}
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public TicketOverViewViewModel()
{
ticketDataService = new TicketDataService();
LoadData();
LoadCommands();
}
private void LoadData()
{
tickets = ticketDataService.GetAllTickets().ToObservableCollection();
}
public ICommand EditCommand { get; set; }
private void LoadCommands()
{
EditCommand = new CustomCommand(EditTicket, CanEditTicket);
}
private void EditTicket(object obj)
{
//TODO
}
private bool CanEditTicket(object obj)
{
return true;
}
}
This is how my Customcommand looks:
public class CustomCommand : ICommand
{
private Action<object> execute;
private Predicate<object> canExecute;
public CustomCommand(Action<object> execute, Predicate<object> canExecute)
{
this.execute = execute;
this.canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
bool b = canExecute == null ? true : canExecute(parameter);
return b;
}
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
}
remove
{
CommandManager.RequerySuggested -= value;
}
}
public void Execute(object parameter)
{
execute(parameter);
}
}
My View:
<Window x:Class="DataGridMVVM.View.TicketOverView"
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:DataGridMVVM.View"
mc:Ignorable="d"
Title="Ticket Overview" Height="348.936" Width="421.277"
DataContext= "{Binding Source={StaticResource mainViewModelLocator}, Path=TicketOverViewViewModel }">
<Grid>
<DataGrid x:Name="dataGrid" HorizontalAlignment="Left" VerticalAlignment="Top"
AutoGenerateColumns="True" ItemsSource="{Binding Tickets}" IsReadOnly="True"
CanUserResizeColumns="True" Height="100" Width="400" />
<Button x:Name="button" Content="Edit" Command="{Binding EditCommand}"
HorizontalAlignment="Left" Margin="239,183,0,0" VerticalAlignment="Top" Width="75" />
</Grid>
</Window>
Swap around the read only attributes on each cell. So start like this
dataGridView1.Rows[row.index].Cells[column.index].ReadOnly= true;
then on button click
dataGridView1.Rows[row.index].Cells[column.index].ReadOnly= false;
you can set the whole row or column like this
dataGridView1.Rows[row.index].ReadOnly= false;
dataGridView1.Columns[columns.index].ReadOnly= false;
or the entire grid
dataGridView1.ReadOnly = false;
Trying to learn WPF and I have read/tested with a tutorial.
This is my scenario:
A wpf C# application.
My main window has a UserControl on it.
This UserControl has 4 buttons on it.
My intent is to bind each command(click) event to each button.
But instead of binding each button to its own class I want to bind each command event of these 4 buttons to 1 class.
So.. I wanted to pass a parameter to the CanExecute and Execute methods and I was/am trying to pass an enum to these methods.
so.. what i have got so far is this:
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object parameter)
{
var commandChosen= parameter as TopMenuCommands;
return true;
}
public void Execute(object parameter)
{
var buttonChosen = parameter as MenuCommandObject;
evMenuChange(buttonChosen);
}
public enum enTopMenuCommands
{
Button1 = 0,
Button1 = 1,
Button1 = 2,
Button1 = 3
}
But how can I tie this to my main window?
I admit I maybe doing this all completely wrong but I am still learning.
thanks
My ICommand implementation takes an Action<object> in the constructor. The Execute method just invoked the Action.
That way the logic for each command is passed in from where it is created.
ICommand Implementation:
public class SimpleCommand : ICommand
{
public event EventHandler CanExecuteChanged;
private Action<object> _execute;
private Func<bool> _canExecute;
public SimpleCommand(Action<object> execute) : this(execute, null) { }
public SimpleCommand(Action<object> execute, Func<bool> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object param)
{
if (_canExecute != null)
{
return _canExecute.Invoke();
}
else
{
return true;
}
}
public void Execute(object param)
{
_execute.Invoke(param);
}
protected void OnCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this,EventArgs.Empty);
}
#region Common Commands
private static SimpleCommand _notImplementedCommand;
public static ICommand NotImplementedCommand
{
get
{
if (_notImplementedCommand == null)
{
_notImplementedCommand = new SimpleCommand(o => { throw new NotImplementedException(); });
}
return _notImplementedCommand;
}
}
#endregion
}
Usage Example:
using System;
using System.Data.Entity;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Windows.Input;
using SqlMetaQuery.Model;
using SqlMetaQuery.ViewModels.ScriptList;
using SqlMetaQuery.Windows.EditQuery;
using WpfLib;
namespace SqlMetaQuery.Windows.Main
{
class MainWindowVm : WpfLib.ViewModel
{
public MainWindowVm()
{
if (!IsInDesignMode)
{
using (Context db = new Context())
{
ScriptTree = new ScriptTreeVm(db.Tags
.Include(t => t.Scripts)
.OrderBy(t => t.Name));
CurrentUser = db.Users.Where(u => u.UserName == "Admin").AsNoTracking().FirstOrDefault();
MiscTag = db.Tags.Where(t => t.Name == "Misc").AsNoTracking().FirstOrDefault();
}
}
}
public ScriptTreeVm ScriptTree { get; }
public Model.User CurrentUser { get; }
private Model.Tag MiscTag { get; }
private void SaveScript(Model.Script script)
{
using (var context = new Model.Context())
{
context.Scripts.Add(script);
context.SaveChanges();
}
}
#region Commands
private ICommand _exitCommand;
public ICommand ExitCommand
{
get
{
if (_exitCommand == null)
{
_exitCommand = new SimpleCommand((arg) => WindowManager.CloseAll());
}
return _exitCommand;
}
}
private ICommand _newScriptCommand;
public ICommand NewScriptCommand
{
get
{
if (_newScriptCommand == null)
{
_newScriptCommand = new SimpleCommand((arg) =>
{
var script = new Model.Script()
{
Title = "New Script",
Description = "A new script.",
Body = ""
};
script.Tags.Add(MiscTag);
var vm = new EditQueryWindowVm(script);
var result = WindowManager.DisplayDialogFor(vm);
// if (result.HasValue && result.Value)
//{
script.VersionCode = Guid.NewGuid();
script.CreatedBy = CurrentUser;
script.CreatedDate = DateTime.Now.ToUniversalTime();
SaveScript(script);
//}
});
}
return _newScriptCommand;
}
}
#endregion
}
}
XAML:
<Window x:Class="SqlMetaQuery.Windows.Main.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:SqlMetaQuery.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:SqlMetaQuery.Windows.Main"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow"
Width="800"
Height="600"
d:DataContext="{d:DesignInstance Type=local:MainWindowVm}"
mc:Ignorable="d">
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="File">
<MenuItem Command="{Binding Path=NewScriptCommand}" Header="New Script..." />
<Separator />
<MenuItem Command="{Binding Path=ExitCommand}" Header="Exit" />
</MenuItem>
</Menu>
<Grid Margin="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="250" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<controls:ScriptTree Grid.Row="0"
Grid.Column="0"
DataContext="{Binding Path=ScriptTree}" />
<GridSplitter Grid.Row="0"
Grid.Column="1"
Width="8"
VerticalAlignment="Stretch"
ResizeBehavior="PreviousAndNext" />
<Grid Grid.Row="0" Grid.Column="2">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="8" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border Grid.Row="0" Style="{StaticResource BorderStandard}">
<TextBlock Text="{Binding Path=ScriptTree.CurrentScript.Title}" />
</Border>
<Border Grid.Row="3" Style="{StaticResource BorderStandard}">
<TextBlock Text="{Binding Path=ScriptTree.CurrentScript.Body}" />
</Border>
</Grid>
</Grid>
</DockPanel>
</Window>