Bind a Button Flyout Command to a ViewModel's Command in DataTemplate - c#

I'm new to UWP I am attempting to bind to an event in my ViewModel from a button flyout inside a listview that is shown on every item. I've looked at many solutions online and came up with the following code, it compiles fine but when I click the said Edit button nothing happens.
My ViewModel is available from the Page's context and not the Item's context
XAML
<ListView x:Name="MainListView"
ItemsSource="{x:Bind ViewModel.Devices, Mode=OneWay}"
SelectionMode="Multiple"
SelectionChanged="MainListView_SelectionChanged">
<ListView.ItemTemplate>
<DataTemplate>
<Grid Width="Auto">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0*"></ColumnDefinition>
<ColumnDefinition Width=".4*"></ColumnDefinition>
<ColumnDefinition Width="3*"></ColumnDefinition>
<ColumnDefinition Width="3*"></ColumnDefinition>
<ColumnDefinition Width="3*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="2" Text="{Binding AssetNumber}"/>
<TextBlock Grid.Column="3" Text="{Binding SerialNumber}"/>
<TextBlock Grid.Column="4" Text="{Binding Model}"/>
<Button Grid.Column="1" Height="30" Width="30">
<Button.Flyout>
<MenuFlyout>
<MenuFlyoutItem Text="Edit" Icon="Edit"
Command="{Binding ElementName=MainListView,Path=DataContext.ViewModel.EditCommand}"
CommandParameter="{Binding}"/>
</MenuFlyout>
</Button.Flyout>
</Button>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
View Model Class
public class MainPageViewModel
{
// Elements contained in the main listview
public ObservableCollection<Device> Devices = new ObservableCollection<Device>();
public MainPageViewModel()
{
DeviceProvider.Fill(ref Devices, 100);
EditCommand = new RelayCommand<Device>(EditDevice);
}
public RelayCommand<Device> EditCommand { get; set; }
private async void EditDevice(Device device)
{
// Code here that creates a dialog
}
}
The Device class
public class Device : INotifyPropertyChanged
{
private string assetNumber;
private string serialNumber;
private string model;
public string AssetNumber
{
get
{
return assetNumber;
}
set
{
assetNumber = value;
OnPropertyChanged();
}
}
public string SerialNumber
{
get
{
return serialNumber;
}
set
{
serialNumber = value;
OnPropertyChanged();
}
}
public string Model
{
get
{
return model;
}
set
{
model = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
The RelayCommand class
public class RelayCommand<T> : ICommand
{
private readonly Action<T> _execute;
private readonly Func<bool> _canExecute;
public event EventHandler CanExecuteChanged;
public RelayCommand(Action<T> execute) : this(execute, null)
{
}
public RelayCommand(Action<T> execute, Func<bool> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute();
}
public void Execute(object parameter)
{
_execute((T)parameter);
}
public void RaiseCanExecuteChanged()
{
var handler = CanExecuteChanged;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
}

Your code doesn't seem to have any problems. So it should work perfectly. But if not, I'd suspect MainPage.ViewModel member might not be defined properly. The property to be used in {Binding} must be "public" and must have "get" accessor.
public sealed partial class MainPage : Page
{
public MainPageViewModel ViewModel { get; set; } = new MainPageViewModel();
public MainPage()
{
this.InitializeComponent();
DataContext = this;
}
}

it compiles fine but when I click the said Edit button nothing happens.
The problem is that you bind wrong Path(Path=DataContext.ViewModel.EditCommand) for MenuFlyoutItem, please remove ViewModel field. And I have edited your code please refer the following.
<Page.DataContext>
<local:MainPageViewModel x:Name="ViewModel"/>
</Page.DataContext>
......
<Button
Grid.Column="1"
Width="30"
Height="30"
>
<Button.Flyout>
<MenuFlyout>
<MenuFlyoutItem
Command="{Binding ElementName=MainListView, Path=DataContext.EditCommand}"
CommandParameter="{Binding}"
Icon="Edit"
Text="Edit"
/>
</MenuFlyout>
</Button.Flyout>
</Button>

Related

Why doesn't my view update when adding items to the ObservableCollection

So I have this project where I have two buttons and a ListView. The ListView is separated into it's own UserControl with it's own ViewModel which contains a ObservableCollection.
I'm using a ContentPresenter to display that control because I will be using different views.
Currently, when I'm clicking the Log button, it does in fact add the string to the collection, but the view doesn't update. And it keeps adding more and more everytime I click on it. (I've put a breakpoint inside private void AddItemOne() to inspect it to prove that it adds items.)
Question
Why doesn't my view update when I click the "Log" button even though it's adding items.
It does add the first item if I hardcode it like this.
public LogViewModel()
{
Logs = new ObservableCollection<string>();
Logs.Add("Test");
}
MainWindow.xaml
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="25"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Button Grid.Row="0" Grid.Column="0" Height="25" Content="Log"
Command="{Binding AddItemOneCommand}"/>
<Button Grid.Row="0" Grid.Column="1" Height="25" Content="Other"
Command="{Binding AddItemTwoCommand}"/>
<UserControl Content="{Binding CurrentView}" Grid.Row="1" Grid.ColumnSpan="2"/>
</Grid>
MainViewModel.cs
class MainViewModel
{
public RelayCommand AddItemOneCommand { get; set; }
public RelayCommand AddItemTwoCommand { get; set; }
private object _currentView;
public object CurrentView
{
get { return _currentView; }
set
{
_currentView = value;
}
}
/* ViewModels */
public LogViewModel LogViewModel { get; set; }
public MainViewModel()
{
AddItemOneCommand = new RelayCommand(o => AddItemOne());
AddItemTwoCommand = new RelayCommand(o => AddItemTwo());
LogViewModel = new LogViewModel();
_currentView = LogViewModel;
}
private void AddItemOne()
{
LogViewModel.Logs.Add("Test");
}
private void AddItemTwo()
{
LogViewModel.Logs.Add("Test");
}
}
LogView.xaml
<UserControl.DataContext>
<local:LogViewModel/>
</UserControl.DataContext>
<Grid>
<ListView ItemsSource="{Binding Logs}" Background="Gray"/>
</Grid>
LogViewModel.cs
class LogViewModel
{
public ObservableCollection<string> Logs { get; set; }
public LogViewModel()
{
Logs = new ObservableCollection<string>();
}
}
Misc
App.xaml
<Application.Resources>
<DataTemplate DataType="{x:Type local:LogViewModel}">
<local:LogView/>
</DataTemplate>
</Application.Resources>
And the RelayCommand
public class RelayCommand : ICommand
{
private Action<object> execute;
private Func<object, bool> canExecute;
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
{
this.execute = execute;
this.canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return this.canExecute == null || this.canExecute(parameter);
}
public void Execute(object parameter)
{
this.execute(parameter);
}
}
Remove this from LogView.xaml:
<UserControl.DataContext>
<local:LogViewModel/>
</UserControl.DataContext>
It creates another instance of the LogViewModel instead of using the one that you create in the MainViewModel.
You should also replace the UserControl in MainWindow.xaml with a ContentControl that binds to the CurrentView property:
<ContentControl Content="{Binding CurrentView}" Grid.Row="1" Grid.ColumnSpan="2" />

WPF MVVM with multiple model instances

There are lots of MVVM examples out there but I can't apply one to my case because I have several instances of the same classes. Also, I need to manipulate the models directly so I can subscribe some Observer's to their Observable's.
I've simplified my case for the question.
I've three classes from my model : Lamp, Switch and Grid.
They can interact thanks to an Observer/Observable mechanism.
Basically, activating a switch switches on/off all the lamps connected to the same grid.
I want to make a window which shows a specific usage of these classes. There should Button's bound to Switch's and TextBlock's bound to Lamp's.
How can I bind each instance to the UI component I've prepared for it ?
Here is a simple case I'd like to build an UI for :
Grid entranceGrid = new Grid("Entrance Grid");
Lamp hallLamp = new Lamp("Hall Lamp");
Lamp stairsLamp = new Lamp("Stairs Lamp");
Switch downSwitch = new Switch("Downstair Switch");
Switch upSwitch = new Switch("Upstair Switch");
downSwitch.Subscribe(entranceGrid);
upSwitch.Subscribe(entranceGrid);
entranceGrid.Subscribe(hallLamp);
entranceGrid.Subscribe(stairsLamp);
// Below are four instances I'd like to bind to some UI component.
LampViewModel hallLampVM = new LampViewModel(hallLamp);
LampViewModel stairsLampVM = new LampViewModel(stairsLamp);
SwitchViewModel downSwitchVM = new downSwitchVM(downSwitch);
SwitchViewModel upSwitchVM = new downSwitchVM(upSwitch);
Here is my full code if you want to play with it (VS 2017)
This answer is ugly but it does work and maybe it will help someone to come with something better.
I will implement this solution for the time being. Maybe, I'll just work on something better to handle the NotifyPropertyChanged.
The window XAML :
<Window.DataContext>
<vm:MainViewModel />
</Window.DataContext>
<Grid Margin="0,0,0,0">
<Grid.RowDefinitions>
<RowDefinition Height="2*" />
<RowDefinition Height="3*" />
<RowDefinition Height="2*" />
</Grid.RowDefinitions>
<GroupBox Grid.Row="0" Header="Entrée">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="Hall d'entrée"/>
<TextBlock Grid.Row="1" Grid.Column="0" Text="Escalier"/>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding TxtHallLamp}"/>
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding TxtStairsLamp}"/>
<Button Grid.Row="0" Grid.Column="2" Content="rez" Command="{Binding DownSwitchCommand}"/>
<Button Grid.Row="1" Grid.Column="2" Content="1er" Command="{Binding UpSwitchCommand}"/>
</Grid>
</GroupBox>
[Few lines you don't need]
</Grid>
The ViewModelBase along the MainViewModel :
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public bool NotifyPropertyChanged<T>(ref T variable, T value, [CallerMemberName] string propertyName = null)
{
if (object.Equals(variable, value)) return false;
variable = value;
NotifyPropertyChanged(propertyName);
return true;
}
}
class MainViewModel : ViewModelBase
{
private Lamp _HallLamp, _StairsLamp;
private Switch _DownSwitch, _UpSwitch;
private IRelayCommand _DownSwitchCommand, _UpSwitchCommand;
public MainViewModel()
{
#region entrance
Grid entranceGrid = new Grid("Entrance Grid");
_HallLamp = new Lamp("Hall Lamp");
_StairsLamp = new Lamp("Stairs Lamp");
_DownSwitch = new Switch("Downstair Switch");
_UpSwitch = new Switch("Upstair Switch");
_DownSwitch.Subscribe(entranceGrid);
_UpSwitch.Subscribe(entranceGrid);
entranceGrid.Subscribe(_HallLamp);
entranceGrid.Subscribe(_StairsLamp);
#endregion // entrance
}
private string LampToTxt(Lamp lamp)
{
return lamp.Light ? "ON" : "OFF";
}
public string TxtHallLamp
{
get
{
return LampToTxt(_HallLamp);
}
}
public string TxtStairsLamp
{
get
{
return LampToTxt(_StairsLamp);
}
}
private void NotifyEntranceGridPropertyChanged()
{
NotifyPropertyChanged(nameof(TxtHallLamp));
NotifyPropertyChanged(nameof(TxtStairsLamp));
}
public IRelayCommand DownSwitchCommand
{
get
{
return _DownSwitchCommand ?? (_DownSwitchCommand = new RelayCommand(
() => {
_DownSwitch.Press();
NotifyEntranceGridPropertyChanged();
},
() => true));
}
}
public IRelayCommand UpSwitchCommand
{
get
{
return _UpSwitchCommand ?? (_UpSwitchCommand = new RelayCommand(
() => {
_UpSwitch.Press();
NotifyEntranceGridPropertyChanged();
},
() => true));
}
}
}
And the interface IRelayCommand along the class RelayCommand :
public interface IRelayCommand : ICommand
{
void RaiseCanExecuteChanged();
}
class RelayCommand : IRelayCommand
{
private Action _Execute;
private Func<bool> _CanExecute;
public event EventHandler CanExecuteChanged;
public RelayCommand(Action Execute) : this(Execute, null)
{
}
public RelayCommand(Action Execute, Func<bool> CanExecute)
{
if (Execute == null)
throw new ArgumentNullException();
_Execute = Execute;
_CanExecute = CanExecute;
}
public bool CanExecute(object parameter)
{
return (_CanExecute == null) ? true : _CanExecute();
}
public void Execute(object parameter)
{
_Execute();
}
public void RaiseCanExecuteChanged()
{
if (CanExecuteChanged != null)
{
CanExecuteChanged(this, EventArgs.Empty);
}
}
}

ListView SelectedItems binding: why the list is always null

I'm developing a UWP App, with Mvvm Light and Behaviours SDK. I defined a multi selectable ListView:
<ListView
x:Name="MembersToInviteList"
IsMultiSelectCheckBoxEnabled="True"
SelectionMode="Multiple"
ItemsSource="{Binding Contacts}"
ItemTemplate="{StaticResource MemberTemplate}">
</ListView>
I'd like, with a button binded to a MVVM-Light RelayCommand, to obtain a list with the selected items:
<Button
Command="{Binding AddMembersToEvent}"
CommandParameter="{Binding ElementName=MembersToInviteList, Path=SelectedItems}"
Content="Ok"/>
The RelayCommand (of MVVM-Light framework):
private RelayCommand<object> _addMembersToEvent;
public RelayCommand<object> AddMembersToEvent
{
get
{
return _addMembersToEvent
?? (_addMembersToEvent = new RelayCommand<object>(
(selectedMembers) =>
{
// Test
// selectedMembers is always null!
}));
}
}
I put a breakpoint inside the command, and I notice that selectedMembers is always null, although I select various items. By the console output I don't see any binding error or something else.
Also, if I pass as CommandParameter the whole list, and I put a breakpoint inside command's definition, i notice that I can't access to SelectedItems nor SelecteRanges value.
<DataTemplate x:Name="MemberTemplate">
<Viewbox MaxWidth="250">
<Grid Width="250"
Margin="5, 5, 5, 5"
Background="{StaticResource MyLightGray}"
BorderBrush="{StaticResource ShadowColor}"
BorderThickness="0, 0, 0, 1"
CornerRadius="4"
Padding="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0"
Width="45"
Height="45"
Margin="5,0,5,0"
VerticalAlignment="Center"
CornerRadius="50">
<Grid.Background>
<ImageBrush AlignmentX="Center"
AlignmentY="Center"
ImageSource="{Binding Image.Url,
Converter={StaticResource NullGroupImagePlaceholderConverter}}"
Stretch="UniformToFill" />
</Grid.Background>
</Grid>
<TextBlock Grid.Column="1"
Margin="3"
VerticalAlignment="Center"
Foreground="{StaticResource ForegroundTextOverBodyColor}"
Style="{StaticResource LightText}"
Text="{Binding Alias}" />
</Grid>
</Viewbox>
</DataTemplate>
What's the reason? How can I obtain such list?
One of the solutions to pass SelectedItems from ListView in ViewModel (with RelayCommands) is described in igralli's blog.
Pass ListView SelectedItems to ViewModel in Universal apps
Try the following code to get the selected objects from the parameter.
private RelayCommand<IList<object>> _addMembersToEvent;
public RelayCommand<IList<object>> AddMembersToEvent
{
get
{
return _addMembersToEvent
?? (_addMembersToEvent = new RelayCommand<IList<object>>(
selectedMembers =>
{
List<object> membersList = selectedMembers.ToList();
}));
}
}
I've made a small example for your case without MVVM-Light and it works perfect.
Maybe the problem is within the RelayCommand-class. I've written the following:
public class RelayCommand<T> : ICommand
{
private readonly Action<T> execute;
private readonly Predicate<T> canExecute;
public RelayCommand(Action<T> execute, Predicate<T> canExecute = null )
{
if (execute == null)
throw new ArgumentNullException("execute");
this.execute = execute;
this.canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
if (canExecute == null)
return true;
return canExecute((T) parameter);
}
public void Execute(object parameter)
{
execute((T) parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}
Thanks to Roman's answer I figured out how to solve the issue:
First of all, as Roman suggested:
private RelayCommand<IList<object>> _addMembersToEvent;
public RelayCommand<IList<object>> AddMembersToEvent
{
get
{
return _addMembersToEvent
?? (_addMembersToEvent = new RelayCommand<IList<object>>(
selectedMembers =>
{
List<object> membersList = selectedMembers.ToList();
}));
}
}
Then, the XAML:
<Button
Command="{Binding AddMembersToEvent}"
CommandParameter="{Binding ElementName=MembersToInviteList, Converter={StaticResource ListViewSelectedItemsConverter}}"
Content="Ok"/>
The difference here is that I passed the whole list as parameter, not it's SelectedItems property. Then, using an IValueConverter I converted from the ListView object to IList<object> of SelectedMember:
public class ListViewSelectedItemsConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var listView = value as ListView;
return listView.SelectedItems;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
In this way the RelayCommand<IList<object>> got the right list and not a null value.
I can't find a reason but you can easily skirt the issue altogether by having an "IsSelected" property on your Contact object and putting a check box on your template:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<ListView ItemsSource="{Binding Contacts}">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<CheckBox Content="{Binding Name}" IsChecked="{Binding IsSelected, Mode=TwoWay}"></CheckBox>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<TextBlock Grid.Row="1" Text="{Binding SelectedItemsOutput}"></TextBlock>
<Button Grid.Row="2" Content="What is checked?" Command="{Binding GoCommand}"></Button>
</Grid>
and VMs etc:
public class MainViewModel : INotifyPropertyChanged
{
private string _selectedItemsOutput;
public ObservableCollection<Contact> Contacts { get; set; } = new ObservableCollection<Contact> { new Contact() { Id = 1, Name = "Foo" }, new Contact() { Id = 2, Name = "Bar" } };
public ICommand GoCommand => new RelayCommand(Go);
public string SelectedItemsOutput
{
get { return _selectedItemsOutput; }
set
{
if (value == _selectedItemsOutput) return;
_selectedItemsOutput = value;
OnPropertyChanged();
}
}
void Go()
{
SelectedItemsOutput = string.Join(", ", Contacts.Where(x => x.IsSelected).Select(x => x.Name));
}
public event PropertyChangedEventHandler PropertyChanged;
}
public class Contact : INotifyPropertyChanged
{
private bool _isSelected;
public int Id { get; set; }
public string Name { get; set; }
public bool IsSelected
{
get { return _isSelected; }
set
{
if (value == _isSelected) return;
_isSelected = value;
OnPropertyChanged();
}
}
public override string ToString()
{
return Name;
}
public event PropertyChangedEventHandler PropertyChanged;
}
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
DataContext = new MainViewModel();
}
}
Just my five cents and might be an absolutely long shot, but you should check this:
If the ItemsSource implements IItemsRangeInfo, the SelectedItems collection is not updated based on selection in the list. Use the SelectedRanges property instead.
I am just guessing based on Tomtom's answer, since he says he did almost exactly as you and got a working solution. Maybe the difference is in this little detail. I haven't checked it myself yet.

Change item checkbox on ListBox selection change

I have a ListBox created by ItemTemplate and Binding
<controls:PanoramaItem Header="{Binding AppResources.SettingsSubpage2, Source={StaticResource LocalizedStrings}}" HeaderTemplate="{StaticResource HeaderTemplate}">
<Grid>
<ListBox x:Name="DayOfWeekSelector" ItemTemplate="{StaticResource DayOfWeekTemplate}" ItemsSource="{Binding DayOfWeekElementList}" Foreground="{StaticResource AppForegroundColor}" LostFocus="DayOfWeekSelector_LostFocus" HorizontalAlignment="Left" Width="420" />
</Grid>
</controls:PanoramaItem>
Template code:
<phone:PhoneApplicationPage.Resources>
<!--- ... --->
<DataTemplate x:Key="DayOfWeekTemplate">
<Grid Height="65" Width="332">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<CheckBox IsChecked="{Binding IsActive, Mode=TwoWay}" Tag="{Binding}" d:LayoutOverrides="Width, Height" BorderBrush="{StaticResource AppBackgroundColor}" Background="{StaticResource ScheduleBackgroundAccentsColor}" Grid.Column="0" Unchecked="CheckBox_Unchecked" />
<StackPanel Grid.Column="1" Orientation="Horizontal">
<TextBlock Text="{Binding Name}" VerticalAlignment="Center" d:LayoutOverrides="Width"/>
<TextBlock Text="{Binding TaskCounter, Mode=OneWay, Converter={StaticResource DayOfWeekCounter}}" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="10,0,0,0"/>
</StackPanel>
</Grid>
</DataTemplate>
<!--- ... --->
And it's working fine. I've got all my items on the list. Checkboxes are binded to appropriate elements (clicking on it is changing proper value).
But by default ListBox can be also selected. Selection high-light TextBox binded to Name but don't change CheckBox (binded to IsActive). How can I tie item selection changing to checkbox state changing (in Silverlight)?
Edit:
public partial class SettingsPage : PhoneApplicationPage, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
public List<DayOfWeekElement> DayOfWeekList
{
get
{
return CyberSyncPlanBase.Instance.DayOfWeekElementList;
}
set
{
CyberSyncPlanBase.Instance.DayOfWeekElementList = value;
NotifyPropertyChanged("DayOfWeekList");
}
}
public SettingsPage()
{
InitializeComponent();
DayOfWeekSelector.DataContext = CyberSyncPlanBase.Instance;
}
private void DayOfWeekSelector_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
DayOfWeekElement dowe = (DayOfWeekElement) DayOfWeekSelector.SelectedItem;
if (dowe != null)
dowe.IsActive = (dowe.IsActive) ? false : true;
}
And in singleton INotifyPropertyChanged i've implemented in the same way:
private List<DayOfWeekElement> dayOfWeekElementList;
public List<DayOfWeekElement> DayOfWeekElementList
{
get { return dayOfWeekElementList; }
set
{
dayOfWeekElementList = value;
RecalcWeekTasks();
NotifyPropertyChanged("DayOfWeekElementList");
}
}
Bottom class:
public class DayOfWeekElement
{
public string Name { get { return this.DayOfWeek.ToStringValue(); } }
public bool IsNotEmpty { get { return (TaskCounter > 0); } }
public int TaskCounter { get; set; }
public bool IsActive { get; set; }
public DayOfWeek DayOfWeek { get; set; }
}
I think you could use the SelectedItem property of the ListBox control.
A possible implementation could be this:
Subscribe to the event SelectedIndexChanged of the ListBox.
Get the selected item.
For the selected item, change its IsActive property to true.
This works if the interface INotifyPropertyChanged is implemented in your data class.
E.g.:
public class DayOfWeekElement : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private bool isActive = false;
public bool IsActive {
get
{
return this.isActive;
}
set
{
if (value != this.isActive)
{
this.isActive= value;
NotifyPropertyChanged("IsActive");
}
}
}
}

Display the list view on button command

I having the following xaml and code of the view model,currently I bind the screen list view to the view model .
the user control have text box and button and when the user click on the button (Go) I want to get the data from the view ,how should I do that?
currently I always get the data when I run the window but
I want the list to be empty when I open the page and when I click on
the GO button the list will be filled
<Grid Width="877" Height="632"
DataContext="{Binding Source={StaticResource ConfigServiceModelViewDataSource}}" >
<Grid.ColumnDefinitions>
<UserControl.Resources>
<ViewModel:ConfigServiceModelView x:Key="ConfigServiceModelViewDataSource" />
<DataTemplate x:Key="CollectionTemplate">
</DataTemplate>
</UserControl.Resources>
<ListView Grid.Column="2" HorizontalAlignment="Center" Height="230"
Margin="5,20,0,0" Grid.Row="2" VerticalAlignment="Top" Width="330"
ItemsSource="{Binding GetCollection}" }" >
<Button Content="Go" Grid.Column="3" Grid.Row="1" HorizontalAlignment="Left"
VerticalAlignment="Top" Width="75" Height="21.96" />
in the ModelView Im getting the data from the model like
internal class ConfigModelView {
private ConfigServiceModel _configServiceModel = new ConfigServiceModel();
public List<string> GetServiceCollection {
get {
return _configServiceModel.CollectList;
}
}
}
Try this
ViewModel
public class ConfigModelView
{
public ConfigModelView()
{
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();
}
}
Custom Command .You can use RelayCommand
public class MyCommand : ICommand
{
private Action _action;
private Func<bool> _canExecute;
public MyCommand(Action action, Func<bool> canExecute)
{
_action = action;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute();
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_action();
}
public void RaiseCanExecuteChanged()
{
if(CanExecuteChanged!=null)
CanExecuteChanged(this,new EventArgs());
}
}
xaml
<Button Content="Go" Grid.Column="3" Grid.Row="1" HorizontalAlignment="Left"
VerticalAlignment="Top" Width="75" Height="21.96" Command="{Binding GoCommand}"/>
I hope this will help.

Categories