Get the commandparameter value when clicking a button MVVM - c#

Other examples are not working or are completly different from what I do.
So I have a button on my XAML page like this:
<Button Width="100"
Height="50"
Margin="0 0 10 0"
Command="{Binding MenuButtonViewModel.MenuButtonCommand, Source={StaticResource Locator}}"
CommandParameter="{Binding Parameter}"
IsEnabled="{Binding IsEnabled}">
And on my ViewModel this:
public RelayCommand MenuButtonCommand
{
get
{
return new RelayCommand(() =>
{
});
}
}
The question is how do I get the value of the commandparameter on my ViewModel?
public RelayCommand<String> MenuButtonCommand
{
get
{
return new RelayCommand((parameter) =>
{
Text = parameter;
});
}
}
This is not working, have no idea how to do this without having to use codebehind to pass the commandparameter value to the ViewModel.

Ok found the solution:
public RelayCommand<string> Command
{
get
{
return new RelayCommand<string>(parameter =>
{
var str = parameter;
});
}
}
Thanks guys.

Related

WPF - How to trigger an event for each element(button) in a ListBox?

I'd like to trigger event for the elemens which is button in a listbox, the view is as below. It is a listbox with 32 buttons.The purpose is to toggle the button between 0-1 and trigger event for each element.
There are two solutions I am thinking about. The solution one is to set command for each button, it works but the problem is I can't catch a selectedItem or a selectedIndex successfully so that even thougn I know the button is toggled, but I don't know the item index.
<ListBox x:Name="lbDirection"
Grid.Row="1"
Grid.Column="1"
SelectionMode="Extended"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
Margin="30,26,44,83"
Style="{StaticResource ListBoxHorz}"
ItemContainerStyle="{StaticResource ItemNoBorder}"
SelectedItem="{Binding SelectedLineItem}"
ItemsSource="{Binding ButtonList}"
IsSynchronizedWithCurrentItem="True">
<ListBox.ItemTemplate>
<DataTemplate>
<Button Style="{StaticResource ChangeButton}"
Command="{Binding DataContext.ToggleDirectionCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType= ListBox}}">
<Button.Content>
<TextBlock Text="{Binding BtnText}"
TextDecorations="Underline" />
</Button.Content>
</Button>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
ViewModel:
public class DigitalIOViewModel : BindableBase
{
public ICommand ToggleDirectionCommand { get; set; }
public ICommand ToggleStateCommand { get; set; }
private ObservableCollection<LineButtons> btnlist;
private LineButtons _selectedLineItem;
public LineButtons SelectedLineItem
{
get { return _selectedLineItem; }
set {
_selectedLineItem=value;
OnPropertyChanged("SelectedLineItem");
}
}
public DigitalIOViewModel()
{
btnlist = new ObservableCollection<LineButtons>();
CreateButtonList();
ToggleDirectionCommand = new RelayCommand(ToggleDirectionAction);
}
private void ToggleDirectionAction()
{
LineButtons selectedLineItem = SelectedLineItem;
int lineIndex=(SelectedLineItem!= null && ButtonList!=null)? ButtonList.IndexOf(SelectedLineItem) : -1;
if (selectedLineItem.BtnText == "1" && lineIndex == 0)
{
ButtonList[lineIndex] = new LineButtons() { BtnText = "0" };
}
else
ButtonList[lineIndex] = new LineButtons() { BtnText = "1" };
}
public ObservableCollection<LineButtons> ButtonList
{
get { return btnlist; }
set { btnlist = value; OnPropertyChanged("ButtonList"); }
}
public void CreateButtonList()
{
for (int i = 0; i < 32; i++)
{
ButtonList.Add(new LineButtons() { BtnText = "1"});
}
}
}
public class LineButtons : BindableBase
{
private string btnText;
public string BtnText
{
get { return btnText; }
set { btnText = value; OnPropertyChanged("BtnText"); }
}
}
I cannot get a correct selectedIndex from this code.
The second solution is to use the IsSelected/SelectedItem property instead of button command binding, I was wondering if there is conflict with the button command binding for item and IsSelected property fot listbox,so we can't use them at the same time, can someone help me out which is the best solution? Thanks in advance.
In addition to the Command property you can set the CommandParameter to the current LineButtons item (class names should be singular, names of collections use plural):
<ListBox.ItemTemplate>
<DataTemplate>
<Button Style="{StaticResource ChangeButton}"
Command="{Binding DataContext.ToggleDirectionCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType= ListBox}}"
CommandParameter="{Binding}">
<Button.Content>
<TextBlock Text="{Binding BtnText, UpdateSourceTrigger=PropertyChanged}"
TextDecorations="Underline" />
</Button.Content>
</Button>
</DataTemplate>
</ListBox.ItemTemplate>
Then get the current item, the item that triggered the command, by accessing the command parameter:
private void Execute(object commandParameter)
{
var currentLineButton = commandParameter as LineButtons;
...
}

wpf mvvm - TextBox/TextBlock/CheckBox that is bound to IsSelected - I need to "grab" the input there to create a new object

My textbox in xaml is this:
<TextBox Grid.Row="1" Grid.Column="1" Width="225" x:Name="cat" Margin="10"
Text="{Binding SelectedCategory.Category, Mode=TwoWay}" />
And in my viewmodel I have the following:
public string Category
{
get => _category;
set
{
_category = value;
OnPropertyChanged(() => Category);
}
}
private EventCategory _selectedCategory;
public EventCategory SelectedCategory
{
get => _selectedCategory;
set
{
_selectedCategory = value;
OnPropertyChanged(() => SelectedCategory);
}
}
public ICommand UpdateCommand { get { return new BaseCommand(ClickUpdate); } }
private async void ClickUpdate()
{
ShowMessageBox("You clicked Update!");
ButtonIsEnabled = false;
Id = SelectedCategory.Id;
Category = SelectedCategory.Category;
IsActive = SelectedCategory.IsActive;
var service = new DataService<EventCategory>(new TimeKeeprDbContextFactory());
EventCategory eventCategory = await service.Get(Id);
if (eventCategory == null)
{
ShowMessageBox("There is no such category. Please add a new one or chose another");
}
else
eventCategory = await service.Update(Id, eventCategory);
}
But of course, I'm getting an error on Category = SelectedCategory.Category;
What do I need to be able to do to "grab" the contents of the textbox (and the textblock and checkbox, of course) so that I can call my Update(Id) method?
I have it working in another class, but there my textbox was binding directly to the property:
<TextBox Grid.Row="3" Grid.Column="2" Height="18" VerticalAlignment="Center"
Text="{Binding Mode=Default, UpdateSourceTrigger=PropertyChanged, Source={StaticResource viewModel},
Path=Password, ValidatesOnExceptions=true, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" />
I'm new to mvvm and data binding, so please be gentle :D
I found the problem in a section of my View that I didn't think was relevant - I had to change
<UserControl.Resources>
<me:CategoriesViewModel x:Key="viewModel"/>
</UserControl.Resources>
to:
<UserControl.DataContext>
<me:CategoriesViewModel/>
</UserControl.DataContext>
That and a couple other small tweaks got the whole thing working.

C# MVVM: Edit a SelectedItem of an ObservableCollection with a RelayCommand

I am quiet new to programming and am currently learning C# and the MVVMC pattern (which is I think basically the same as MVVM pattern).
I need to code a database tool for ChiliPlants for university. There you should be able to edit an existing item from an ObservableCollection.
This ObservableCollection I displayed in a DataGrid, see this:DataGrid
Below the DataGrid there are three buttons: Add, Edit and Delete.
I was able to programm the AddButton, aswell as the DeleteButton.
Unfortunately I don't know how to programm the EditButton.
It should open a new window, where the SelectedItem should be opened like this:EditWindow
Until now my EditButton does the same thing as my AddButton.
See my code here:
View:
<StackPanel Grid.Row="1" Orientation="Horizontal">
<Button Content="Add" Margin="5,5,0,5" Width="100" Command="{Binding AddCommand}" />
<Button Content="Edit" Margin="5,5,0,5" Width="100" Command="{Binding EditCommand}" />
<Button Content="Delete" Margin="5,5,540,5" Width="100" Command="{Binding DeleteCommand}" />
<Button Content="Sichern" Margin="5,5,5,5" Width="100" Command="{Binding SaveCommand}" />
</StackPanel>
ViewModel:
private ICommand _editCommand;
public ICommand EditCommand
{
get { return _editCommand; }
set { _editCommand = value; }
}
Controller:
public void SDInitialize()
{
var view = new WindowStammdatenverwaltung();
mViewModel = new WindowStammdatenverwaltungViewModel
{
EditCommand = new RelayCommand(EditCommandExecute, EditCommandCanExecute)
};
view.DataContext = mViewModel;
view.ShowDialog();
}
private void EditCommandExecute(object obj)
{
var editedObject = new WindowEditController().EditChiliModel();
if (editedObject != null)
{
mViewModel.Stock.Add(mViewModel.SelectedChili);
}
}
private bool EditCommandCanExecute(object obj)
{
return mViewModel.SelectedChili != null;
}
The problem is with the EditCommandExecute. Currently I have just put the Code for an AddCommandExecute in there. I unfortunately don't know how to code such an EditCommandExecute.
My WindowEditController looks like this:
public class WindowEditController
{
WindowEdit mView;
public ChiliModel EditChiliModel()
{
mView = new WindowEdit();
WindowEditViewModel mViewModel = new WindowEditViewModel
{
ChiliModel = new ChiliModel(),
OkCommand = new RelayCommand(ExecuteOkCommand),
CancelCommand = new RelayCommand(ExecuteCancelCommand),
};
mView.DataContext = mViewModel;
if (mView.ShowDialog() == true)
{
return mViewModel.ChiliModel;
}
else
{
return null;
}
}
private void ExecuteOkCommand(object obj)
{
mView.DialogResult = true;
mView.Close();
}
private void ExecuteCancelCommand(object obj)
{
mView.DialogResult = false;
mView.Close();
}
I know, that I could let the user edit the SelectedItem inside the DataGrid, but this is not allowed in my task...
Could I maybe use the same window as for my AddCommand? Basically they should look the same, the EditWindow should just already contain the information of the SelectedItem.
I looked up almost every entry similar to this topic, but I did not find a simple solution. Or a solution which I was able to understand with my bad coding skills :( ...
I would be very happy if you guys could help me. Please keep it simple for this newbie :)
What I already tried:
I tried to add a CommandParameter to my Button looking like this: CommandParameter="{Binding SelectedItem, ElementName=StockDataGrid}"
But this still didn't open the window containing the data of the SelectedItem. It just opened a completely new Window for a new Item.
Use CommandParameter just after the Command property, and bind it to the SelectedItem of the DataGrid.
For example, suppose that you DataGrid has the attribute Name=MyDataGrid.
The Button becomes:
<Button Content="Edit"
Margin="5,5,0,5"
Width="100"
Command="{Binding EditCommand}"
CommandParameter="{Binding SelectedItem, ElementName=MyDataGrid}"/>
When EditCommandExecute(object obj) is executed, obj is actually the current SelectedItem that you want.

ListView strange behavoiur with blend trigger

I have simple ListView with trigger also I am using mvvm light RelayCommand.
The main question is why when function ChangeShowingMode(ObservableCollection<Employee> items) fires in console it displays only Firing: 0. Why it do not show also Firing: 1,Firing: 5,Firing: 11,Firing: 99. How to make it?
xmlns:i="http://schemas.microsoft.com/expression/2010/interactions"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactivity"
<Grid>
<ListView ItemsSource="{Binding Items}">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<CheckBox IsChecked="{Binding IsChecked}" VerticalAlignment="Center">
<ei:Interaction.Triggers>
<ei:EventTrigger EventName="Checked">
<ei:InvokeCommandAction Command="{Binding DataContext.IsCheckedTrueCommand,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type Views:MainWindow}}}" CommandParameter="{Binding .}"/>
</ei:EventTrigger>
</ei:Interaction.Triggers>
</CheckBox>
<Label Grid.Column="1" Content="{Binding Name}" VerticalAlignment="Center"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
Code Looks like:
namespace WpfApplication
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ListViewModel();
}
}
public class Employee : ViewModelBase
{
private bool isChecked;
public bool IsChecked
{
get
{
return this.isChecked;
}
set
{
this.isChecked = value;
this.RaisePropertyChanged("IsChecked");
}
}
public string Name { get; set; }
}
public class ListViewModel : ViewModelBase
{
private ObservableCollection<Employee> items;
public ObservableCollection<Employee> Items
{
get
{
return this.items;
}
set
{
this.items = value;
this.RaisePropertyChanged("Items");
}
}
public ListViewModel()
{
Items = new ObservableCollection<Employee>();
LoadAsync();
}
public void LoadAsync()
{
Task t = null;
t = Task.Factory.StartNew(new Action(() =>
{
System.Threading.Thread.Sleep(5000);
ObservableCollection<Employee> temporaryItems = new ObservableCollection<Employee>();
for (int i = 0; i < 100; i++)
{
temporaryItems.Add(new Employee { IsChecked = false, Name = i.ToString() });
}
Items = temporaryItems;
}));
t.ContinueWith((key) => { ChangeShowingMode(Items); });
}
public void ChangeShowingMode(ObservableCollection<Employee> items)
{
Items[0].IsChecked = true;
Items[1].IsChecked = true;
Items[5].IsChecked = true;
Items[11].IsChecked = true;
Items[99].IsChecked = true;
}
public RelayCommand<Employee> IsCheckedTrueCommand
{
get
{
return new RelayCommand<Employee>((emp) => Command(emp));
}
}
public void Command(Employee emp)
{
Console.WriteLine("Firing: {0}", emp.Name);
}
}
}
The Checked Event will raise only for visible Checkboxes. So the command is only called for the visible ones.
By test your provided example, the Properties are set by the task and also reflected as expected by the UI. But the Command is only called, when I manually check a checkbox, not through initialisation.
Changing
t.ContinueWith((key) => { ChangeShowingMode(Items); });
to
t.ContinueWith((key) => { Syste.Threading.Thread.Sleep(5000); ChangeShowingMode(Items); });
has the result, that the Command is called for the visible Checkboxes.
If you scoll down when the checkboxes are displayed, the command is called for Checkbox 99.
But this behavior isn't strange at all. I think, it is a timming-Issue when your viewmodel is connected to the Datacontext or not.
The answer to your question "How to make it?" depends on what is your problem with the current solution.
To watch the property changes, you could
How to Listen to Property Changes of Items of an ObservableCollection
and call a command on changes.
But I think you don't need this at all. The checked-State is reflected by your IsChecked-Property through the Two-Way binding.
<CheckBox IsChecked="{Binding IsChecked, Mode=TwoWay}" VerticalAlignment="Center">
So you can easily get them by
var checkedItemsList = Items.Where(ele => ele.IsChecked).ToList();
And if you make Changes by Code, you can call the Command manually in your viewmodel.
Items[0].IsChecked = true;
if (IsCheckedTrueCommand.CanExecute(Items[0]))
IsCheckedTrueCommand.Execute(Items[0]);
But I'm not sure if I get you here. The question remains what the command should do in an enhanced scenario.
Btw, which Version of Framework you are using? See check box binding not working on .NET 3.5 SP1
And to call a Comand on User-Interaction you can use:
<CheckBox CommandParameter="{Binding}"
Command="{Binding DataContext.CheckBoxCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"
IsChecked="{Binding IsChecked, Mode=TwoWay}" VerticalAlignment="Center">

How to pass a UserControl with CommandParameter?

I have using a DelegateCommand and i want to pass the UserControl with Command.
#region OpenViewCommand
private DelegateCommand<UserControl> _openViewCommand;
public ICommand OpenViewCommand
{
get
{
if (_openViewCommand == null)
_openViewCommand = new DelegateCommand<UserControl>(new Action<UserControl>(OpenView));
return _openViewCommand;
}
}
public void OpenView(UserControl ViewName)
{
UserControl ctrl = (UserControl)ViewName;
JIMS.Controls.Message.Show(ViewName.ToString());
}
#endregion
Command in XAML
<Button Name="btnStockGroups" Command="{Binding OpenViewCommand}" CommandParameter="JIMS.View.Stock.StockGroups">stock group</Button>
If you give your UserControl an x:Name (e.g. "MyView"), you should be able to do something like this:
<Button Name="btnStockGroups"
Command="{Binding OpenViewCommand}"
CommandParameter="{Binding ElementName=MyView}">

Categories

Resources