MVVM WPF C# Auto-properties Combobox - c#

I would like to refactor my code a bit with Propertychanged.Fody as shown on this page http://www.mutzl.com/tag/mvvm-light/
Normal code:
private string _platformSelectedItem;
public string PlatformSelectedItem
{
get { return _platformSelectedItem; }
set
{
if (_platformSelectedItem == value) return;
_platformSelectedItem = value;
// Perform any pre-notification process here.
GetData();
RaisePropertyChanged();
}
}
to
public string PlatformSelectedItem {get; private set}
The property is bounded to a Comboxbox and the values for the Combobox are dynamic based on another combobox, therefore I have my Method GetData().
<ComboBox ItemsSource="{Binding Platforms}" SelectedItem="{Binding PlatformSelectedItem, Mode=TwoWay}" Grid.Column="1" Grid.Row="2" Height="20" Grid.ColumnSpan="2" Margin="0,3,15.667,3"/>
If I refactor my code to autoproperties the method must be executed by click / open of the combobox.
Should I use a eventtrigger with command our is a more simpler way possible?

Based on the thread can we use <i:Interaction.Triggers> in WPF MVVM (not in Silverlight)
My final solution looks:
ViewModel:
Properties area:
public RelayCommand SelectionChangedCommand { get; private set; }
Constructor:
SelectionChangedCommand = new RelayCommand(Update);
Method area:
private void Update()
{
GetData();
}
In my UI then:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
<ComboBox ItemsSource="{Binding Platforms}" SelectedItem="{Binding PlatformSelectedItem, Mode=TwoWay}" Grid.Column="1" Grid.Row="2" Height="20" Grid.ColumnSpan="2" Margin="0,3,15.667,3">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding SelectionChangedCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>

Related

How to bind a double click to a listbox item

I have two list boxes that both hold collections. The current setup is so that when a item is selected in the left listbox, you can click a button to add that selected state the right listbox. There is an add and remove button for the listboxes that are tied to a custom command with the listbox selected item being the command parameter.
I would like to add a double click functionality to each box so that items can be double clicked to add and remove. I should be able to use my current command execute methods to do this, but have not found a solution to implementing this into a listbox, or listboxitem. I would like to follow MVVM as much as possible, but I've already side stepped that a bit with the current execute methods as i'll show below, but any help would be appreciated. I have not had luck finding anything regarding my specific issue.
<ListBox x:Name="List" ItemContainerStyle="{StaticResource ListBoxItem}" DataContext="{StaticResource VM}"
ItemsSource="{Binding Names, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" DisplayMemberPath="Name"
Style="{StaticResource ResourceKey=ListBox}"/>
<Button Content=">>" Margin="5" Style="{StaticResource ResourceKey=MultiButton}"
CommandParameter="{Binding ElementName=List}"
Command="{Binding Path=AddSelectedItemCommand}"/>
public void AddSelectedItem(object obj)
{
ListBox ListBox = obj as ListBox;
List<Type> List = new List<Type>();
if (Name == null)
Name = new ObservableCollection<Type>();
if (Name != null)
{
foreach (Type item in ListBox.SelectedItems.Cast<object>().ToList())
{
List.Add(item);
Names.Remove(item);
}
foreach (Type listItem in List)
{
var state = Name.FirstOrDefault(aa => aa.Name == listItem.Name);
if (state == null)
{
Name.Add(listItem);
}
}
}
OnPropertyChanged("Name");
OnPropertyChanged("Names");
}
Firstly I would like to let you know that your View Model should know nothing at all about the View itself, so it should know nothing about ListBoxes.
Objects should only know about they things which they depend upon, and not those which depend upon it. Therefore the ViewModel should only know about the collections of data which it is making available to any client.
In your example, what happens when the control is changed from a ListBox -you will have to change your Command.
So, first things first, you will need to change your view model implementation, what you have currently is not MVVM.
Here is an entire listing which should help you along your way:
<Window x:Class="WpfExample.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:WpfExample"
mc:Ignorable="d"
Title="MainWindow" Height="140" Width="410">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ListBox ItemsSource="{Binding Path=Names, Mode=OneWay}"
SelectedItem="{Binding Path=SelectedName}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding .}">
<TextBlock.InputBindings>
<MouseBinding MouseAction="LeftDoubleClick"
Command="{Binding Path=DataContext.MyDoubleClickCommand,
RelativeSource={RelativeSource AncestorType=Window, Mode=FindAncestor} }" />
</TextBlock.InputBindings>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ListBox Grid.Column="1" Margin="10,0,0,0" ItemsSource="{Binding Path=NamesTwo, Mode=OneWay}"
SelectedItem="{Binding Path=SelectedNameTwo}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding .}">
<TextBlock.InputBindings>
<MouseBinding MouseAction="LeftDoubleClick"
Command="{Binding Path=DataContext.MyOtherDoubleClickCommand,
RelativeSource={RelativeSource AncestorType=Window, Mode=FindAncestor} }" />
</TextBlock.InputBindings>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
And the code behind
namespace WpfExample
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MyViewModel();
}
}
}
Then there is the ViewModel, which you should notice only modifies the collections which are exposed for the View to consume
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;
using Prism.Commands;
namespace WpfExample
{
public class MyViewModel : INotifyPropertyChanged
{
private string _selectedName;
private string _selectedNameTwo;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public ObservableCollection<string> Names { get; }
= new ObservableCollection<string>(new List<string>
{
"Name1",
"Name2",
"Name3",
"Name4",
"Name5"
});
public ObservableCollection<string> NamesTwo { get; } = new ObservableCollection<string>(new List<string>());
public string SelectedName
{
get { return _selectedName; }
set { _selectedName = value; OnPropertyChanged(); }
}
public string SelectedNameTwo
{
get { return _selectedNameTwo; }
set { _selectedNameTwo = value; OnPropertyChanged(); }
}
public ICommand MyOtherDoubleClickCommand
{
get
{
return new DelegateCommand<string>(name =>
{
NamesTwo.Remove(name);
Names.Add(name);
SelectedNameTwo = "";
});
}
}
public ICommand MyDoubleClickCommand
{
get
{
return new DelegateCommand<string>(name =>
{
Names.Remove(name);
NamesTwo.Add(name);
SelectedName = "";
});
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
I have used the Prism.Core package for the DelegateCommand object. This is not essential, I just did it for ease
You don't even need the SelectedName and SelectedNameTwo properties if they will not be used whilst processing the ViewModel. I included them for completeness.
.
Edited:
I did not originally notice that this is for a UWP project. I believe the following will work -though it is untested here since I am not set up for UWP on my machine at the moment. I'm not certain of the DoubleClick EventName.
<Page xmlns:i="using:Microsoft.Xaml.Interactivity"
xmlns:core="using:Microsoft.Xaml.Interactions.Core>
<ListBox Grid.Column="1" Margin="10,0,0,0" ItemsSource="{Binding Path=NamesTwo, Mode=OneWay}"
SelectedItem="{Binding Path=SelectedNameTwo}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding .}" >
<i:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="DoubleClick">
<core:InvokeCommandAction Command="{Binding Path=DataContext.MyDoubleClickCommand,
RelativeSource={RelativeSource AncestorType=Page, Mode=FindAncestor} }"
CommandParameter="{Binding .}" />
</core:EventTriggerBehavior>
</i:Interaction.Behaviors>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Credit to Bill, as the UWP edit pointed me toward a satisfactory solution.
Firstly, I added a NuGet reference to Microsoft.Xaml.Behaviors.Uwp.Managed
Secondly I added the namespaces Bill mentions to the xaml in which my control is located:
xmlns:i="using:Microsoft.Xaml.Interactivity"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
Then I added some XAML in my control (List View in this example):
<ListView ...>
<i:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="DoubleTapped">
<core:InvokeCommandAction Command="{Binding NavigateUpCommand, RelativeSource={RelativeSource Mode=TemplatedParent}}" />
</core:EventTriggerBehavior>
</i:Interaction.Behaviors>
...
</ListView>
In my case, this was a templated control - and the "DoubleTapped" event name was used successfully :)
The Command was set up in the best way I know; made available as an ICommand get accessor on the in the control class, which used a stock "RelayCommand" implementation

Wpf ListView trigger SourceUpdate

How do i trigger the SourceUpdate of my wpf listview?
This is my xaml
<ListView DataContext="{StaticResource vmInstance}" SelectedItem="{Binding selectedItem}" BorderBrush="Beige" BorderThickness="1" ItemsSource="{Binding lstExternal}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged" >
<cmd:EventToCommand Command="{Binding Path=ItemChanged}"
PassEventArgsToCommand="True" />
</i:EventTrigger>
<i:EventTrigger EventName="SourceUpdated">
<cmd:EventToCommand Command="{Binding Path=SourceUpdated}"
PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
<ListView.View >
</ListView.View>
</ListView>
ViewModel:
public static RelayCommand SourceUpdated { get; set; }
//on initialize
SourceUpdated = new RelayCommand(SourceUpdateEvent);
public void SourceUpdateEvent()
{
Console.WriteLine("Updated");
}
Why does SourceUpdateEvent doesn't run?
Am I using the wrong event?
Thank you
want to execute a method when the itemsource of the listview is changed or updated
Then bind the ItemsSource property of the ListView to a source property of your view model
<ListView ItemsSource="{Binding YourCollection}" ...>
...and invoke your command in the setter of this source property:
private ObservableCollection<YourType> _sourceCollection;
public ObservableCollection<YourType> SourceCollection
{
get { return _sourceCollection; }
set
{
_sourceCollection = value;
RaisePropertyChanged();
SourceUpdated.Execute(null);
}
}
This is the (only) correct way to solve this using the MVVM pattern.

How to bind MouseDoubleClick event in mvvm of ListView

I have a ListView with multi Columns.
I want to run a command when double click on a row. I use EventTrigger for this. But when i double click on every where of ListView run command. But I want to run this command when double click on ListViewItem.
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<cmd:EventToCommand Command="{Binding ShowLetterCommand}" CommandParameter="{Binding SelectedItem, ElementName=DashboardListView}" />
</i:EventTrigger>
</i:Interaction.Triggers>
You should call CallMethodAction from System.Windows.Interactions library and point at your method name at MethdoName like that:
I've made an example and it perfectly works:).
You need two libraries to handle MouseDoubleClick event:
System.Windows.Interactivity(Address at my computer is C:\Program Files (x86)\Microsoft SDKs\Expression\Blend\.NETFramework\v4.5\Libraries\)
Microsoft.Expression.Interactions(Address at my computer is C:\Program Files (x86)\Microsoft SDKs\Expression\Blend\.NETFramework\v4.5\Libraries\)
XAML:
<UserControl
...the code omitted for the brevity...
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
...the code omitted for the brevity...
>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="5*"/>
</Grid.ColumnDefinitions>
<ListView ItemsSource="{Binding Persons}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<ei:CallMethodAction MethodName="DoubleClickMethod" TargetObject="{Binding}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding IdPerson}" Margin="0,0,5,0"/>
<TextBlock Text="{Binding Name}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</UserControl>
ViewModel:
public void DoubleClickMethod()
{
MessageBox.Show("It is a Double Click");
/* if(parameter!=null)
YourClass aClass=(YourClass)parameter;
*/
}
private ObservableCollection<Person> persons;
public ObservableCollection<Person> Persons
{
get { return persons; }
set
{
persons = value;
OnPropertyChanged("Persons");
}
}
public MainWindowViewModel()
{
LoadPersons();
}
private void LoadPersons()
{
persons = new ObservableCollection<Person>();
for (int i = 0; i < 20; i++)
{
Persons.Add(new Person() { IdPerson = i, Name = "Charlie " + i.ToString()});
}
}
Model:
public class Person
{
public int IdPerson { get; set; }
public string Name { get; set; }
}
The link to download a test project.
you can use Inputbindings to achieve this.
<ListView ItemsSource="{Binding SampleListData,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" SelectedItem="{Binding SelectedTree,Mode=TwoWay}" Name="dgSample">
<ListView.InputBindings>
<MouseBinding MouseAction="LeftDoubleClick" Command="{Binding ListDataEdited}"/>
</ListView.InputBindings>
</ListView>
bind selected row to 'SelectedItem' .
View Model :
#region Commands
public RelayCommand ListDataEdited
{
get
{
return new RelayCommand(listDataEdited);
}
}
#endregion
#region Methods
private void listDataEdited()
{
if (SelectedTree != null)
{
//code here
}
}
#endregion

UWP/MVVM data binding in listview with a button does not work

I have read articles about how commanding works different inside of a listview so I tried that code but when I click nothing happens. I am using Template10. Most of the example I find are for WPF which has incompatible pieces. Just need the bare minimum to get the button click to call the method below. The relevant parts of my code are :
<ListView x:Name="lvMain"
ItemsSource="{Binding LeadSpeakerItems}"
SelectedItem="{Binding Lsi}">
<ListView.ItemTemplate>
...
<Button Content="Details"
Command="{Binding ElementName=Root, Path=RunCommand}"
Grid.Column="1" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
And the code:
public ICommand RunCommand { get; private set; }
public MainPageViewModel()
{
if (Windows.ApplicationModel.DesignMode.DesignModeEnabled)
{
LeadSpeakerItems.Add(new LeadSpeakerItem {
VelocifyLeadTitle = "The is the lead title that says somrthing about something and her a number 234-456-3454",
VelocifyFirstName = "BobbiMinajobi",
VelocifyLastName = "Luciferdissikusliskus",
VelocifyLoanAmount = 254000.00,
VelocifyHomeValue = 278000.00
});
}
RunCommand = new DelegateCommand<object>(OnRunCommand, CanRunCommand);
}
private void OnRunCommand(object obj)
{
// use the SelectedCustomer object here...
}
private bool CanRunCommand(object obj)
{
return true;
}
EDIT 1:
How would I get that particular item when the button or the listview item is selected? I am trying to get this piece of code run when that happens. I am missing something.
set
{
Set(ref selectedItem, value);
}
Supposing Root is your page or another control with your viewmodel as DataContext, you should alter your XAML to:
<Button Content="Details"
Command="{Binding ElementName=Root, Path=DataContext.RunCommand}"
Grid.Column="1" />
as RunCommand itself is not known to your Root object, but DataContext (your vm) is.
<Button Content="Details"
Command="{Binding RunCommand}"
Grid.Column="1" />
or
<ListView
x:Name="lvMain"
DataContext={Binding}>
....
</ListView>
<Button
DataContext="{Binding ElementName=lvMain, Path=DataContext}"
Content="Details"
Command="{Binding RunCommand}"
Grid.Column="1" />
try use Template10.Mvvm.DelegateCommand
for example
in viewmodel
public ICommand ItemSelected
{
get
{
return new Template10.Mvvm.DelegateCommand<string>((s) =>
{
NavigationService.Navigate(typeof(DetailPage), s);
});
}
}
add to your page
<page
xmlns:Behaviors="using:Template10.Behaviors"
xmlns:Core="using:Microsoft.Xaml.Interactions.Core"
xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:vm="using:....ViewModel"
....>
<Page.DataContext>
<vm:ViewModel />
</Page.DataContext>
in your listview
<ListView x:Name="listView" ... ItemsSource="{x:Bind ViewModel.ListItem}" >
<Interactivity:Interaction.Behaviors>
<Core:EventTriggerBehavior EventName="Tapped">
<Core:InvokeCommandAction Command="{x:Bind ViewModel.ItemSelected}" CommandParameter="{Binding ElementName=listView,Path=SelectedItem}"/>
</Core:EventTriggerBehavior>
</Interactivity:Interaction.Behaviors>
</ListView>

Set CommandParameter to currently tapped item on a ListBox (WP7)

I want to set command parameter to currently selected item on a ListBox.
XAML:
<!--<ListBox ItemsSource="{Binding Places}" SelectedItem="{Binding SelectedPlace, Mode=TwoWay}">-->
<ListBox ItemsSource="{Binding Places}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Tap">
<i:InvokeCommandAction Command="{Binding ListBoxClick}" CommandParameter="{Binding}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<ListBox.ItemTemplate>
<DataTemplate>
(...)
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
C# (part of ViewModel code exposing the ListBoxClick command)
public RelayCommand ListBoxClick { get; set; }
ListBoxClick = new RelayCommand((o) => {
//model is always null
var model = o as BasicModel;
SelectedPlace = model;
});
I added appropriate references, and namespace:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
The problem is that in action called by RelayCommand object the o parameter is always null.
UPDATE
C# code for SelectedPlace property
public BasicModel SelectedPlace {
get {
return _selectedPlace;
}
set {
_selectedPlace = value;
RaisePropertyChanged("SelectedPlace");
}
}
When I use this:
<ListBox ItemsSource="{Binding Places}" SelectedItem="{Binding SelectedPlace, Mode=TwoWay}">
everything works fine if I click ListBoxItem for the first time, but when I click on a selected ListBoxItem nothing happens, because selection doesn't change. I need to be able to detect item click in both situations.
I figured out an ugly way to achieve my goal.
XAML
<ListBox ItemsSource="{Binding Places}" SelectedItem="{Binding SelectedPlace, Mode=TwoWay}">
C# (ViewModel)
private bool _placeSelected;
public BasicModel SelectedPlace {
get {
return _selectedPlace;
}
set {
_placeSelected = true;
_selectedPlace = value;
RaisePropertyChanged("SelectedPlace");
}
}
ListBoxClick = new RelayCommand((o) => {
if (!_placeSelected) {
SelectedPlace = _selectedPlace;
}
else {
_placeSelected = false;
}
});
This way RaisePropertyChanged("SelectedPlace"); will be called in both cases.
Try this:
<ListBox ItemsSource="{Binding Places}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Tap">
<i:InvokeCommandAction Command="{Binding ListBoxClick}"
CommandParameter="{Binding SelectedItem, RelativeSource={RelativeSource AncestorType={
x:Type ListBox}}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<ListBox.ItemTemplate>
<DataTemplate>
(...)
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
If that doesn't work, try changing the Binding to this:
CommandParameter="{Binding SelectedItem, RelativeSource={RelativeSource Self}}"
UPDATE >>>
Oh sorry, I didn't see that you were using Windows Phone 7. As an alternative, try adding a property into your code behind/view model that binds to the ListBox.SelectedItem property:
<ListBox ItemsSource="{Binding Places}" SelectedItem="{Binding SelectedItem}" ... />
Then you should be able to do this:
ListBoxClick = new RelayCommand(() => {
SelectedPlace = SelectedItem;
});
UPDATE 2 >>>
I don't know if Windows Phone 7 supports the Binding.ElementName property, but if it does, try this:
<ListBox Name="ListBox" ItemsSource="{Binding Places}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Tap">
<i:InvokeCommandAction Command="{Binding ListBoxClick}"
CommandParameter="{Binding SelectedItem, ElementNameListBox}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<ListBox.ItemTemplate>
<DataTemplate>
(...)
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Categories

Resources