Assum that I have 3 user Control(TIShowNames,TIEnterCode,TIShowFactor).
they have their views and their corresponding viewModel.
all these 3, are in mainwindowView.
Here is my mainwindowView Xaml:
<Controls:TransitionPresenter Name="transContainer" Grid.Row="2" RestDuration="0:0:1" IsLooped="False" Transition="{StaticResource SlideTransition}">
<TabControl Name="TCMain" Background="#00FFFFFF" BorderThickness="0" Padding="0 -5 0 0 ">
<TabItem Name="TIShowNames" Visibility="Collapsed">
<views:NameView x:Name="NameViewElement" />
</TabItem>
<TabItem Name="TIEnterCode" Visibility="Collapsed">
<views:CodeView x:Name="CodeViewElement" />
</TabItem>
<TabItem Name="TIShowFactor" Visibility="Collapsed">
<views:FactorDetailView x:Name="FactorDetailViewElement" />
</TabItem>
</TabControl>
</Controls:TransitionPresenter>
In my old Programming style i used to use this line of code for navigating through tab items(without any pattern):
private void ChangeTabItemTo(TabItem TI)
{
transContainer.ApplyTransition("TCMain", "TCMain");
TCMain.SelectedItem = TI;
}
I have a btn show in "TIShowNames", so when i clicks on that it has to go to "TIShowFactor".
In MVVM, ViewModel does not know any thing about view(this item tab is in its parent view!!!). so how he can change selected Tab Item without violating MVVM??
Another Try:
Changing Selectedindex wont work because of this error:
"System.Windows.Data Error: 40 : BindingExpression path error: 'Index'
property not found on 'object' ''MainWindowViewModel'
(HashCode=22018304)'. BindingExpression:Path=AAA;
DataItem='MainWindowViewModel' (HashCode=22018304); target element is
'TabControl' (Name=''); target property is 'IsSelected' (type
'Boolean')"
Update:
Controls:TransitionPresenter is from Fluid DLL
Update:
I want to hide tab item's header so no one can click the header and navigatoin through header is possibe only via btns in usercontrols
You could define a DataTemplate per view model type in the view:
<TabControl Name="TCMain"
ItemsSource="{Binding ViewModels}"
SelectedItem="{Binding ViewModel}"
Background="#00FFFFFF" BorderThickness="0" Padding="0 -5 0 0 ">
<TabControl.ContentTemplate>
<DataTemplate>
<ContentControl Content="{Binding}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type local:NameViewViewModel}">
<views:NameView />
</DataTemplate>
<DataTemplate DataType="{x:Type local:CodeViewViewModel}">
<views:CodeView />
</DataTemplate>
<DataTemplate DataType="{x:Type local:FactorDetailViewModel}">
<views:FactorDetailView />
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
...and bind the SelectedItem property to a source property that you set in your view model, e.g.:
public object ViewModel
{
get { return _vm; }
set { _vm = value; NotifyPropertyChanged(); }
}
...
ViewModel = new CodeViewViewModel(); //displays the CodeView
Expanding on mm8's answer, this is how I'd do it:
First of all, I would create a BaseViewModel class to be inherited by every view model that will represent each tab of the TabControl.
I like to implement it as an abstract class with an abstract string property called "Title", so I can dynamically create the tabs and display their names (or titles). This class would also implement the NotifyPropertyChanged interface.
public abstract class BaseViewModel : INotifyPropertyChanged
{
public abstract string Title { get; }
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Then I would create each view model inheriting from this base view model. for example:
public class NameViewModel : BaseViewModel
{
public override string Title
{
get
{
return "Name";
}
}
}
You would do the same for the other view models, only changing the "title" property of each of them.
Now I would create the MainView of the application and its corresponding view model.
The MainViewModel would have a collection of BaseViewModels and a "CurrentViewModel" (of type BaseViewModel) and would add all the view models you want to its collection on its constructor, like this:
public class MainViewModel : BaseViewModel
{
public override string Title
{
get
{
return "Main";
}
}
private ObservableCollection<BaseViewModel> _viewModels;
public ObservableCollection<BaseViewModel> ViewModels
{
get { return _viewModels; }
set
{
if (value != _viewModels)
{
_viewModels = value;
OnPropertyChanged();
}
}
}
private BaseViewModel _currentViewModel;
public BaseViewModel CurrentViewModel
{
get { return _currentViewModel; }
set
{
if (value != _currentViewModel)
{
_currentViewModel = value;
OnPropertyChanged();
}
}
}
public MainViewModel()
{
ViewModels = new ObservableCollection<BaseViewModel>();
ViewModels.Add(new NameViewModel());
ViewModels.Add(new CodeViewModel());
ViewModels.Add(new FactorDetailViewModel());
}
}
Finally, your main view would be similar to what mm8 posted:
(Notice the differences from my code to mm8's code: (1) You need to set the DisplayMemberPath of the TabControl to the "Title" property of the BaseViewModels and (2) You need to set the DataContext of the Window to your MainViewModel)
<Window ...>
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<Grid>
<TabControl Name="TCMain"
ItemsSource="{Binding ViewModels}"
DisplayMemberPath="Title"
SelectedItem="{Binding CurrentViewModel}"
Background="#00FFFFFF" BorderThickness="0" Padding="0 -5 0 0 ">
<TabControl.ContentTemplate>
<DataTemplate>
<ContentControl Content="{Binding}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type local:NameViewModel}">
<local:NameView />
</DataTemplate>
<DataTemplate DataType="{x:Type local:CodeViewModel}">
<local:CodeView />
</DataTemplate>
<DataTemplate DataType="{x:Type local:FactorDetailViewModel}">
<local:FactorDetailView />
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
</Window>
Now it should work as expected. Everytime you change the active tab of the TabControl, the SelectedItem property of the control will change to the corresponding view model, which will be templated as its corresponding view.
This approach is called "View Model First" (instead of View First), by the way.
EDIT
If you want to have a button on one of the view models that has a command to change the current view model, this is how you do it:
I suppose you are familiarized with Josh Smith's RelayCommand. If you are not, just search for its implementation on the web.
You will need to create an ICommand property on your MainViewModel, which will be responsible to change the "CurrentViewModel" property:
private ICommand _showFactorDetailCommand;
public ICommand ShowFactorDetailCommand
{
get
{
if (_showFactorDetailCommand == null)
{
_showFactorDetailCommand = new RelayCommand(p => true, p => show());
}
return _showFactorDetailCommand;
}
}
private void show()
{
CurrentViewModel = ViewModels.Single(s => s.Title == "Factor");
}
The show() method above simply searches the collection of view models that has the title "Factor" and set it to the CurrentViewModel, which in turn will be the Content of the ContentControl that acts as the ContentTemplate of your TabControl inside your main view.
Remember that your FactorDetailViewModel should be implemented as follows:
public class FactorDetailViewModel : ViewModelBase
{
public override string Title
{
get
{
return "Factor";
}
}
}
The button inside your "NameView" will bind to this command which is a property of "MainViewModel" using RelativeSource binding:
<Button Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.ShowFactorDetailCommand}" Content="Show Factor" Height="20" Width="60"/>
You could make this command more generic, passing the title of the view model you would like to navigate to as the command parameter:
private ICommand _showCommand;
public ICommand ShowCommand
{
get
{
if (_showCommand == null)
{
_showCommand = new RelayCommand(p => true, p => show(p));
}
return _showCommand;
}
}
private void show(p)
{
var vm = (string)p;
CurrentViewModel = ViewModels.Single(s => s.Title == vm);
}
Then on your views, pass the Command Parameter too:
<Button Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.ShowCommand}" Content="Show Factor" CommandParameter="Factor" Height="20" Width="60"/>
Finally, to hide your TabItems completely, you need to set the ItemContainerStyle of your TabControl so that the Visibility of your TabItems has the value of "Collapsed".
<TabControl.ItemContainerStyle>
<Style TargetType="{x:Type TabItem}">
<Setter Property="Visibility" Value="Collapsed"/>
</Style>
</TabControl.ItemContainerStyle>
Related
How would you add command to a wpf button that is part of ItemsControl and is modifying the ItemsSource itself?
So here is my XAML:
<ItemsControl ItemsSource="{Binding PluginVMs}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<Button x:Name="btnStampDuplicate"
Content="Duplicate this control"
Command="{Binding ?????????}"/>
<!-- other stuff -->
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
And here is my viewmodel:
public ObservableCollection<PluginViewModel> PluginVMs
{
get { return _pluginVMs; }
set
{
if (_pluginVMs != value)
{
_pluginVMs = value;
NotifyPropertyChanged("PluginVMs");
}
}
}
As you can see PluginVMs is collection of PluginViewModel. So I am aware that the Command that is available from the btnStampDuplicate should be implemented inside of PluginViewModel.
However, as the name duplicate suggest, I would like to make a duplicated copy of the currently generated PluginViewModel inside of PluginVMs. What is the best approach to give that kind of functionality to btnStampDuplicate?
it is not necessary to have a command in each item. you can use CommandParameter to pass an item which is a dupe source
inside DataTemplate bind command using ElementName to access DataContext of a higher level
View
<ItemsControl Name="ListPlugins" ItemsSource="{Binding PluginVMs}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<Button x:Name="btnStampDuplicate"
Content="duplicate"
CommandParameter={Binding Path=.}
Command="{Binding Path=DataContext.DupeCmd, ElementName=ListPlugins}"
/>
<!-- other stuff -->
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
ViewModel
public class Vm
{
public ObservableCollection<PluginViewModel> PluginVMs
{
get { return _pluginVMs; }
set
{
if (_pluginVMs != value)
{
_pluginVMs = value;
NotifyPropertyChanged("PluginVMs");
}
}
}
public ICommand DupeCmd { get; private set; }
}
I got a MVVM set up to switch between views. To accomodate for the design, the MainWindow holds a tabcontroller which shows a page accordingly. One of the inner pages is changed when the user presses a button. A visual representation of the setup:
I set a Presenter viewmodel to as the datacontext of StudentView to handle the button event thrown in StudentOverview. That works, but when I want to switch the view I have to set a new datacontext of a specific type. But since Presenter is my datacontext, switching it removes the button's functionality.
What I want is to change the datatemplate without relying on the datacontext.
StudentView.xaml
<Page {...}>
<Page.DataContext>
<viewModels:Presenter/>
</Page.DataContext>
<Page.Resources>
<DataTemplate x:Key="Overview" DataType="{x:Type models:StudentOverviewModel}">
<local:StudentOverview/>
</DataTemplate>
<DataTemplate x:Key="Add" DataType="{x:Type models:StudentAddModel}">
<local:AddStudentControl/>
</DataTemplate>
</Page.Resources>
<ContentPresenter Content="{Binding}"/>
</Page>
StudentView.xaml.cs
public partial class StudentView : Page
{
public StudentView()
{
InitializeComponent();
// This switches the view but disables the button
this.DataContext = new StudentOverviewModel();
if (this.DataContext is Presenter presenter)
{
presenter.PropertyChanged += (object o, PropertyChangedEventArgs e) =>
{
// This switches the view but disables the button
this.DataContext = new StudentAddModel();
};
}
}
}
I can suggest two solutions:
First solution (recommended) would be to add a SelectedContent property of type object (or any other common base type for all view models e.g., IContentModel) to the Presenter view model. Then bind the SelectedContent to the ContentPresenter.Content property:
Presenter.cs
public partial class Presenter : INotifyPropertyChanged
{
public Presenter()
{
// Set default content
this.SelectedContent = new StudentOverviewModel();
}
private object selectedContent;
public object SelectedContent
{
get => this.selectedContent;
set
{
this.selectedContent = value;
OnPropertyChanged();
}
}
// Use ICommand implementation like DelegateCommand
public ICommand LoadContentCommand => new LoadContentCommand(ExecuteLoadContent, CanExecuteLoadContent);
private void ExecuteLoadContent(object param)
{
// Do something ...
// Load the new content on Button clicked
this.SelectedContent = new StudentAddModel();
}
private bool CanExecuteLoadContent => true;
}
StudentView.xaml.cs
public partial class StudentView : Page
{
public StudentView()
{
InitializeComponent();
}
}
StudentView.xaml
<Page {...}>
<Page.DataContext>
<viewModels:Presenter/>
</Page.DataContext>
<Page.Resources>
<DataTemplate DataType="{x:Type models:StudentOverviewModel}">
<local:StudentOverview/>
</DataTemplate>
<DataTemplate ="{x:Type models:StudentAddModel}">
<local:AddStudentControl/>
</DataTemplate>
</Page.Resources>
<ContentPresenter Content="{Binding SelectedContent}"/>
</Page>
StudentOverview.xaml
<UserControl{...}>
<!--- content -->
<Button Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type StudentView}}, Path=DataContext.LoadContentCommand}"/>
</UserControl>
You can safely remove the Key attribute of the DataTemplate (if the DataType is not the same) so that they will apply automatically to any matching data type (implicit DataTemplate).
Another solution would be to move the SelectedContent to StudentView and turn it into a DependencyProperty:
StudentView.xaml.cs
public partial class StudentView : Page
{
public static readonly DependencyProperty SelectedContentProperty = DependencyProperty.Register(
"SelectedContent",
typeof(object),
typeof(StudentView));
public object SelectedContent
{
get => GetValue(SelectedContentProperty);
set => SetValue(SelectedContentProperty, value);
}
public StudentView()
{
InitializeComponent();
// This switches the view without disabling the button
this.SelectedContent = new StudentOverviewModel();
if (this.DataContext is Presenter presenter)
{
presenter.PropertyChanged += (object o, PropertyChangedEventArgs e) =>
{
// This switches the view without disabling the button
this.SelectedContent = new StudentAddModel();
};
}
}
}
StudentView.xaml
<Page {...}>
<Page.DataContext>
<viewModels:Presenter/>
</Page.DataContext>
<Page.Resources>
<DataTemplate DataType="{x:Type models:StudentOverviewModel}">
<local:StudentOverview/>
</DataTemplate>
<DataTemplate ="{x:Type models:StudentAddModel}">
<local:AddStudentControl/>
</DataTemplate>
</Page.Resources>
<ContentPresenter Content="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type StudentView}}, Path=SelectedContent}"/>
</Page>
How would you add command to a wpf button that is part of ItemsControl and is modifying the ItemsSource itself?
So here is my XAML:
<ItemsControl ItemsSource="{Binding PluginVMs}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<Button x:Name="btnStampDuplicate"
Content="Duplicate this control"
Command="{Binding ?????????}"/>
<!-- other stuff -->
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
And here is my viewmodel:
public ObservableCollection<PluginViewModel> PluginVMs
{
get { return _pluginVMs; }
set
{
if (_pluginVMs != value)
{
_pluginVMs = value;
NotifyPropertyChanged("PluginVMs");
}
}
}
As you can see PluginVMs is collection of PluginViewModel. So I am aware that the Command that is available from the btnStampDuplicate should be implemented inside of PluginViewModel.
However, as the name duplicate suggest, I would like to make a duplicated copy of the currently generated PluginViewModel inside of PluginVMs. What is the best approach to give that kind of functionality to btnStampDuplicate?
it is not necessary to have a command in each item. you can use CommandParameter to pass an item which is a dupe source
inside DataTemplate bind command using ElementName to access DataContext of a higher level
View
<ItemsControl Name="ListPlugins" ItemsSource="{Binding PluginVMs}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<Button x:Name="btnStampDuplicate"
Content="duplicate"
CommandParameter={Binding Path=.}
Command="{Binding Path=DataContext.DupeCmd, ElementName=ListPlugins}"
/>
<!-- other stuff -->
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
ViewModel
public class Vm
{
public ObservableCollection<PluginViewModel> PluginVMs
{
get { return _pluginVMs; }
set
{
if (_pluginVMs != value)
{
_pluginVMs = value;
NotifyPropertyChanged("PluginVMs");
}
}
}
public ICommand DupeCmd { get; private set; }
}
In order to solve a navigation issue in my application I have used an Event Aggregator which has solved the problem but has created an other one.
To navigate between different UserControls I used the Rachel's code you can find here which was working fine until I made some changes.
On the side of my screen I have a Main Menu (HomeViewModel()), by clicking on the items I switch between UserControls and in each of these UserControls there is a another menu bar where I can switch between other UserControls.
But this second menu (CateringMenuViewModel()) doesn't work anymore. The UserControl is displayed but nothing is happening when I am clicking in the menu bar.
At the first sight I thought it's because there is no DataContext.
So I added it in the code behind like this:
public CateringMenuView()
{
InitializeComponent();
this.DataContext = new CateringMenuViewModel(ApplicationService.Instance.EventAggregator);
}
But it still doesn't work.
I don't understand, the property Name is well bounded because the names are displayed in the menu but the command ChangePageCommand is not.
HomeViewModel
public class HomeViewModel : ObservableObject
{
#region Fields
private ICommand _changePageCommand;
private IPageViewModel _currentPageViewModel;
private List<IPageViewModel> _pageViewModels;
#endregion
public HomeViewModel()
{
// Add available pages
PageViewModels.Add(new HomeOrderViewModel());
PageViewModels.Add(new CateringMenuViewModel(ApplicationService.Instance.EventAggregator));
PageViewModels.Add(new HomeAdminViewModel());
// Set starting page
CurrentPageViewModel = PageViewModels[0];
}
#region Properties / Commands
}
CateringMenuViewModel
public class CateringMenuViewModel : ObservableObject, IPageViewModel
{
protected readonly IEventAggregator _eventAggregator;
public CateringMenuViewModel(IEventAggregator eventAggregator)
{
this._eventAggregator = eventAggregator;
PageViewModels.Add(new NewRegularOrderViewModel(ApplicationService.Instance.EventAggregator));
PageViewModels.Add(new NewDeliveryComOrderViewModel());
PageViewModels2.Add(new FillOrderViewModel());
// Set starting page
CurrentUserControl = PageViewModels[0];
this._eventAggregator.GetEvent<GoToFillOrder>().Subscribe(GoToFillOrder);
}
public string Name
{
get
{
return "Catering";
}
}
public string imageSource
{
get
{
return "catering.ico";
}
}
#region Fields
private List<IUserContentViewModel> _pageViewModels;
public List<IUserContentViewModel> PageViewModels
{
get
{
if (_pageViewModels == null)
_pageViewModels = new List<IUserContentViewModel>();
return _pageViewModels;
}
}
private IUserContentViewModel _currentUserControl;
public IUserContentViewModel CurrentUserControl
{
get { return _currentUserControl; }
set
{
if (value != _currentUserControl)
{
_currentUserControl = value;
OnPropertyChanged("CurrentUserControl");
}
}
}
#region Methods
private void ChangeViewModel(IUserContentViewModel viewModel)
{
if (!PageViewModels.Contains(viewModel))
PageViewModels.Add(viewModel);
CurrentUserControl = PageViewModels
.FirstOrDefault(vm => vm == viewModel);
var x = this.GetHashCode();
}
#endregion
private ICommand _changePageCommand;
#endregion
public ICommand ChangePageCommand
{
get
{
if (_changePageCommand == null)
{
_changePageCommand = new RelayCommand(
p => ChangeViewModel((IUserContentViewModel)p),
p => p is IUserContentViewModel);
}
return _changePageCommand;
}
}
private void GoToFillOrder(int i)
{
CurrentUserControl = PageViewModels2[0];
}
}
CateringMenuView
<UserControl.Resources>
<DataTemplate DataType="{x:Type cvm:NewDeliveryComOrderViewModel}">
<cv:NewDeliveryComOrderView/>
</DataTemplate>
<DataTemplate DataType="{x:Type cvm:NewRegularOrderViewModel}">
<cv:NewRegularOrderView/>
</DataTemplate>
<DataTemplate DataType="{x:Type cvm:FillOrderViewModel}">
<cv:FillOrderView/>
</DataTemplate>
</UserControl.Resources>
<Grid Margin="5">
<Grid>
<StackPanel>
<Menu>
<MenuItem Header="New Order">
<ItemsControl ItemsSource="{Binding PageViewModels}" Width="168" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock>
<Hyperlink Command="{Binding ChangePageCommand, Mode=OneWay}" CommandParameter="{Binding}" TextDecorations="{x:Null}">
<InlineUIContainer>
<TextBlock Text="{Binding Name}"/>
</InlineUIContainer>
</Hyperlink>
</TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</MenuItem>
</Menu>
</StackPanel>
</Grid>
<ContentControl Content="{Binding CurrentUserControl}"/>
</Grid>
Two problems here.
First off, you do not want to set the .DataContext of your UserControl manually because you want to use the CateringMenuViewModel from PageViewModels[1], not create a new instance of it.
So definitely remove the line of code
DataContext = new CateringMenuViewModel(ApplicationService.Instance.EventAggregator);
Second problem is why your event is not firing. I took a look at your code in your question's version history, and I do not see you broadcasting the event anywhere.
This line of code is correct to say "any time an event of type GoToFillOrder is broadcast, run the method GoToFillOrder"
_eventAggregator.GetEvent<GoToFillOrder>().Subscribe(GoToFillOrder);
however I don't see any code which actually broadcasts that event. You need a line of code like the following to broadcast the GoToFillOrder message to throughout your application :
_eventAggregator.GetEvent<GoToFillOrder>().Publish();
I finally found the solution.
In CateringMenuView(), I have replaced
<Hyperlink Command="{Binding ChangePageCommand, Mode=OneWay}"
CommandParameter="{Binding}"
TextDecorations="{x:Null}">
by
<Hyperlink Command="{Binding DataContext.ChangePageCommand, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
CommandParameter="{Binding}"
TextDecorations="{x:Null}">
Big thanks to Rachel!
I have a UserControl with a Button and a ListView.
Model
public class Item
{
private string _name = string.Empty;
public string Name
{
get
{
return _name;
}
set
{
_name = value;
}
}
}
ViewModel
public class ViewModel : NotifyProperty
{
private Command addCommand;
public ICommand AddCommand
{
get
{
if (addCommand == null)
addCommand = new Command(addItem);
return addCommand;
}
}
private ObservableCollection<Item> _itemCollection;
public ViewModel()
{
ItemCollection = new ObservableCollection<Item>();
Item newItem = new Item();
newItem.Name = "Joe";
ItemCollection.Add(newItem);
}
public ObservableCollection<Item> ItemCollection
{
get
{
return _itemCollection;
}
set
{
_itemCollection = value;
OnPropertyChanged("ItemCollection");
}
}
private void addItem(Object obj)
{
Item newItem = new Item();
newItem.Name = "Chris";
ItemCollection.Add(newItem);
}
}
UserControl (XAML)
<UserControl.DataContext>
<local:ViewModel />
</UserControl.DataContext>
<UserControl.Resources>
<DataTemplate x:Key="ItemTemplate">
<StackPanel Orientation="Vertical">
<Label Content="{Binding Name}" />
</StackPanel>
</DataTemplate>
</UserControl.Resources>
<Grid>
<DockPanel>
<Button Width="100" Height="30" Content="Add" Command="{Binding AddCommand}" DockPanel.Dock="Top" />
<ListView ItemTemplate="{StaticResource ItemTemplate}" ItemsSource="{Binding ItemCollection}" />
</DockPanel>
</Grid>
I then add this to my MainWindow like so
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.mainContentControl.Content = new ListControl();
}
}
This works fine and when I click the "Add" button the name "Chris" gets added to the ListView.
Now I add a button to MainView and bind its Command property to my ViewModel like so:
<Grid>
<DockPanel>
<Button Width="100" Height="30" Content="Add" Command="{Binding AddCommand}" DockPanel.Dock="Top">
<Button.DataContext>
<local:ViewModel />
</Button.DataContext>
</Button>
<ContentControl x:Name="mainContentControl" />
</DockPanel>
</Grid>
When I click this button in the MainWindow the command is sent to the ViewModel, the addItem event gets called, the name "Chris" gets added to the ItemCollection, but the ListView doesn't update. What am I doing wrong?
Is your ViewModel being set as the data context of another element somewhere else (either in XAML or code-behind).
Where you're setting it as the data context against the button, that will instantiate a new instance of the view model, so any interaction with the instance the button has access to will not update across other instances.
The button will inherit the data context from ancestor elements (e.g. the window etc), so you shouldn't need to set it, but if you do need a separate data context for the button, then I'd recommend creating the instance of ViewModel as a resource and then just referencing that for the elements that need access to it.