I am new to using RelayCommands (following Josh Smith's MVVMDemoApp) and could use some help identifying my mistake.
I have two listboxes. When an item in the first is selected and the "Add" button is pressed, the AddCommand is executed and the second list's ObservableCollection gets the selectedItem added to it.
My View:
<DockPanel >
<Border DockPanel.Dock="Bottom" Height="50" HorizontalAlignment="Left" Width="150" >
<!--Notice here that the Button was disabled until it was given a DataContext, which allowed the CanAddPN to be true-->
<Button Command="{Binding Path=AddToPartsBinCommand}" Content="Add >" />
</Border>
<UniformGrid Columns="2" Rows="1" DockPanel.Dock="Top" >
<!--ListBox 1 (PartNumbersCollection)-->
<ListBox Background="PaleGoldenrod"
ItemsSource="{Binding PnsCollection, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding SelectedPartNumber, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding pn}">
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<!--ListBox 2 (SelectedPartNumbersCollection)-->
<ListBox ItemsSource="{Binding PartsBinCollection, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding pn}">
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</UniformGrid>
</DockPanel>
My ViewModel:
//DummyDBEntities _context;
public ObservableCollection<PartNumber> _pnsCollection;
public ObservableCollection<PartNumber> _partsBinCollection;
PartNumber _selectedPN;
public ICommand _addToPartsBinCommand;
public MasterViewModel(DummyDBEntities _context)
{
_context = new DummyDBEntities();
this._pnsCollection = new ObservableCollection<PartNumber>(_context.PartNumbers);
this._partsBinCollection = new ObservableCollection<PartNumber>();
}
public ObservableCollection<PartNumber> PnsCollection
{
get { return this._pnsCollection; }
set
{
_pnsCollection = value;
OnPropertyChanged("PnsCollection");
}
}
public PartNumber SelectedPN
{
get { return this._selectedPN; }
set
{
this._selectedPN = value;
OnPropertyChanged("SelectedPN");
OnPropertyChanged("PartsBinCollection");
}
}
public ObservableCollection<PartNumber> PartsBinCollection
{
get
{
if (_partsBinCollection == null)
{
_partsBinCollection = new ObservableCollection<PartNumber>();
}
return this._partsBinCollection;
}
set
{
this._partsBinCollection = value;
OnPropertyChanged("PartsBinCollection");
}
}
public ICommand AddToPartsBinCommand
{
get
{
if (_addToPartsBinCommand == null)
_addToPartsBinCommand = new RelayCommand(() => this.AddPN(),
() => this.CanAddPN());
return this._addToPartsBinCommand;
}
}
private bool CanAddPN()
{
return true;
}
private void AddPN()
{
if (this._partsBinCollection == null)
{
this._partsBinCollection = new ObservableCollection<PartNumber>();
}
this._partsBinCollection.Add(this._selectedPN);
}
Thanks in advance!
Also: why would:
private bool CanAddPN()
{
return this._selectedPN != null;
}
leave my Add button permanently disabled? What am I not doing to let the button know that an item has been selected? This seem like it is the same missing link to my understanding of why the command isn't firing ever.
Thanks again!
You need to raise the CanExecuteChanged on your command to let the client know that it should check again to see if it can execute. Not sure about the RelayCommand but I would assume it's something along the lines of mycommand.RaiseCanExecuteChanged();
Don't forget to cast your command to a relaycommand first since you have it exposed as ICommand.
OOPS! Right after posting this after an hour of struggling I realized that in my View I was referring to the selectedItem property "SelectedPartNumber" and not "SelectedPN". This solved both problems. CanExecuteChanged is evaluated already.
Related
I am new to WPF and losing my mind with issues. I have a view, viewmodel and model. I want the user user to fill in some information in the view, press button to confirm and then have a new instance of the model (with the user specified parameters) added to the ObservableCollection and to my local database.
View: (unrelated stuff hidden)
<TextBox DataContext="{DynamicResource RiderequestViewModel}" Margin="15,0,15,0" Grid.Column="1" Grid.Row="1" FontSize="12" Height="25" Text="{Binding Riderequest.Time}"/>
<TextBox DataContext="{DynamicResource RiderequestViewModel}" Margin="15,0,15,0" Grid.Column="1" Grid.Row="2" FontSize="12" Height="25" Text="{Binding Riderequest.LocationFrom}"/>
<TextBox DataContext="{DynamicResource RiderequestViewModel}" Margin="15,0,15,0" Grid.Column="1" Grid.Row="3" FontSize="12" Height="25" Text="{Binding Riderequest.LocationTo}"/>
<Button DataContext="{DynamicResource RiderequestViewModel}" x:Name="nextBtn" Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="5" Content="Verder" Width="150" Foreground="White" Command="{Binding AddRiderequestCommand}" Click="NextBtn_Click"/>
ViewModel RiderequestViewModel:
namespace Drink_n_Drive.ViewModel
{
class RiderequestViewModel: BaseViewModel
{
private Riderequest riderequest;
private ObservableCollection<Riderequest> riderequests;
public ObservableCollection<Riderequest> Riderequests
{
get
{
return riderequests;
}
set
{
riderequests= value;
NotifyPropertyChanged();
}
}
public Riderequest Riderequest
{
get
{
return riderequest;
}
set
{
riderequest= value;
NotifyPropertyChanged();
}
}
public ICommand AddRiderequestCommand { get; set; }
public ICommand ChangeRiderequestCommand { get; set; }
public ICommand DeleteRiderequestCommand { get; set; }
public RiderequestViewModel()
{
LoadRiderequests(); //load existing from DB
LinkCommands(); //Link ICommands with BaseCommands
}
private void LoadRiderequests()
{
RiderequestDataService riderequestDS = new RiderequestDataService();
Riderequests= new ObservableCollection<Riderequests>(riderequestDS .GetRiderequests());
}
private void LinkCommands()
{
AddRiderequestCommand = new BaseCommand(Add);
ChangeRiderequestCommand = new BaseCommand(Update);
DeleteRiderequestCommand = new BaseCommand(Delete);
}
private void Add()
{
RiderequestDataService riderequestDS = new RitaanvraagDataService();
riderequestDS.InsertRiderequest(riderequest); //add single (new) instance to the DB
LoadRiderequests(); //Reload ObservableCollection from DB
}
private void Update()
{
if (SelectedItem != null)
{
RiderequestDataService riderequestDS = new RiderequestDataService();
riderequestDS.UpdateRiderequest(SelectedItem);
LoadRiderequests(); //refresh
}
}
private void Delete()
{
if (SelectedItem != null)
{
RiderequestDataService riderequestDS = new RiderequestDataService();
riderequestDS.DeleteRiderequest(SelectedItem);
LoadRiderequests();
}
}
private Riderequest selectedItem;
public Riderequest SelectedItem
{
get { return selectedItem; }
set
{
selectedItem = value;
NotifyPropertyChanged();
}
}
}
}
Pressing the button simply does nothing and I don't know why. I also have a diffrent page where I want to show a datagrid of all instances in the ObservableCollection like this:
<DataGrid Grid.Row="1" Grid.ColumnSpan="2" Grid.RowSpan="3" DataContext="{DynamicResource RitaanvragenViewModel}" ItemsSource="{Binding Ritaanvragen}" SelectedItem="{Binding SelectedItem}" />
But the grid just shows completly empty. I have added some dummydata to my DB but still doesn't work.
My appologies for the mix of English and Dutch in the code.
I'm not 100% sure about it but i would do something like this:
As for first step I would change the TextBox to look like this:
<TextBox DataContext="{DynamicResource Ritaanvraag}" Margin="15,0,15,0" Grid.Column="1" Grid.Row="1" FontSize="12" Height="25" Text="{Binding Path=Time, Mode=OneWayToSource}"/>
There's no need to pass your ViewModel to it as a DataSource because your View's first few meta-data related lines should already define what ViewModel does it belong to.
When you not specify the type of your binding, it will use a default binding type which depends on the current object. You're using a TextBox now so it will have a TwoWay binding by default.
If you only want to accept data from the user and you don't want to show the data if your model has any then you should use OneWayToSource. (Note: OneWay is a direction between source -> view.)
I would also remove the DataSource from your DataGrid because you already set it's ItemSource:
<DataGrid Grid.Row="1" Grid.ColumnSpan="2" Grid.RowSpan="3" ItemsSource="{Binding Ritaanvragen}" SelectedItem="{Binding SelectedItem}" />
I have asked before about a problem regarding my binding with the checkbox.Finally a step forward! :) Now,I am wondering the following thing:I have a collection named SelectedCourses that stores all the courses checked in the checkbox. My problem is that when I check 2 or more courses,it only stores the first value from the list,even if I selected it after another value. My code for storing the values looks sth like this:
public void SaveTeacher(object param)
{
using (DatabaseStudentsEntitiesLastStand db = new DatabaseStudentsEntitiesLastStand())
{
Cours c = new Cours();
RegisterTeacher t = new RegisterTeacher();
if (c.IsChecked == true)
{
foreach(var item in Courses)
{
if(item.IsChecked)//here IsChecked is true
{
SelectedCourses = new ObservableCollection<Cours>();
SelectedCourses.Add(item);//here I have only one course
}
SelectedCourses.ToList();
}
}
t.SNTeacher = SNTeacher;
t.UserName = _UserName;
t.pwd = pwd;
t.fullName = fullName;
t.education = education;
db.RegisterTeachers.Attach(t);
try
{
db.SaveChanges();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
And my collection is this:
private ObservableCollection<Cours> selectedCourses;
public ObservableCollection<Cours> SelectedCourses
{
get { return selectedCourses; }
set
{
selectedCourses = value;
NotifyOnPropertyChange("SelectedCourses");
}
}
I also have a collection that holds all the courses:
private ObservableCollection<Cours> _courses;
public ObservableCollection<Cours> Courses
{
get => _courses;
set
{
_courses = value;
NotifyOnPropertyChange(nameof(Courses));
}
}
This is how my system looks and an example of checking 2 values:
And this is what i get when I set the breakpoint:
As you can see,only one course is added to SelectedCourses.How can I make it so that it will save as many courses as the user checks?
And last but not least,the binding with the .xaml:
<ListBox x:Name="coursesList" SelectedItem="{Binding SelectedCourses}"
HorizontalAlignment="Left" Margin="538,23.2,0,0" Grid.Row="2"
VerticalAlignment="Top" Width="225" ItemsSource="{Binding Courses}" Height="255" Grid.RowSpan="2" >
<ListBox.ItemTemplate>
<DataTemplate>
<ListBoxItem IsSelected="{Binding IsChecked}">
<CheckBox x:Name="CheckBoxList" Content="{Binding Path=courseName}" IsChecked="{Binding Path=IsSelected,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
</ListBoxItem>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
In your code you are creating a new ObservableCollection in each iteration.
Try it like this:
if (c.IsChecked == true)
{
SelectedCourses = new ObservableCollection<Cours>();
foreach(var item in Courses)
{
if(item.IsChecked)//here IsChecked is true
{
SelectedCourses.Add(item);//here I have only one course
}
}
}
I have managed to do the method and make a step forward!For those in need,this is the method which I use to get the checked values:
public void CheckVal()
{
SelectedCourses = new ObservableCollection<Cours>();
foreach (var item in Courses)
{
if (item.IsChecked)
{
SelectedCourses.Add(item);
}
}
This is the binding with the View:
<ListBox x:Name="coursesList" SelectionMode="Multiple"
HorizontalAlignment="Left" Margin="538,23.2,0,0" Grid.Row="2"
VerticalAlignment="Top" Width="225" ItemsSource="{Binding Courses}"
Height="255" Grid.RowSpan="2" >
<ListBox.ItemTemplate>
<DataTemplate>
<ListBoxItem IsSelected="{Binding IsChecked,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged,ElementName=CheckBoxList}">
<CheckBox x:Name="CheckBoxList" Content="{Binding Path=courseName}" IsChecked="{Binding Path=IsSelected,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
</ListBoxItem>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And the rest is the same(the 2 collections and the IsChecked value) :).Hope it will be of some help for others.Thank you guys for your help!
I have a checklist view that has 2 ScrollViewers. One checklist is for incomplete items, the other is for complete items. They are populated by 2 separate observable collections and bound to by ItemsControls.
The UserControl has a button, when clicked will move that 'check' to the other collection.
Currently the way I have this setup is in the ViewModel that's the DataContext for the UserControl there is a public event that is subscribed to by the main window's VM by using:
((CheckItemVM) ((CheckListItem) cli).DataContext).CompleteChanged += OnCompleteChanged;
where cli is the checklist item.
then the OnCompleteChanged finds the appropriate View object by using:
foreach (object aCheck in Checks)
{
if (aCheck.GetType() != typeof (CheckListItem)) continue;
if (((CheckListItem) aCheck).DataContext == (CheckItemVM) sender)
{
cliToMove = (CheckListItem) aCheck;
break;
}
}
It's pretty obvious this breaks MVVM and I'm looking for a way around it (CheckListItem is the View, and CheckItemVM is it's DataContext ViewModel). Reasoning for the boxed type is I've got another UserControl that will have instances inside both, which are basically section labels, and I need to be able to sort my observable collections where there is an association between the checklistitem to a specific section by name.
This can be done in MVVM using commands, and bindings....
The idea that I propouse here is to create a command in the Windows view model, that manage the check command, and this command to receive the item view model in the params, then manage the the things in the command. I'm going to show you a simple example, using MvvmLight library:
The model:
public class ItemViewModel : ViewModelBase
{
#region Name
public const string NamePropertyName = "Name";
private string _name = null;
public string Name
{
get
{
return _name;
}
set
{
if (_name == value)
{
return;
}
RaisePropertyChanging(NamePropertyName);
_name = value;
RaisePropertyChanged(NamePropertyName);
}
}
#endregion
#region IsChecked
public const string IsCheckedPropertyName = "IsChecked";
private bool _myIsChecked = false;
public bool IsChecked
{
get
{
return _myIsChecked;
}
set
{
if (_myIsChecked == value)
{
return;
}
RaisePropertyChanging(IsCheckedPropertyName);
_myIsChecked = value;
RaisePropertyChanged(IsCheckedPropertyName);
}
}
#endregion
}
A simple model with two property, one for the name (an identifier) and another for the check status.
Now in the Main View Model, (or Windows view model like you want)....
First the Collections, one for the checked items, and another for the unchecked items:
#region UncheckedItems
private ObservableCollection<ItemViewModel> _UncheckedItems;
public ObservableCollection<ItemViewModel> UncheckedItems
{
get { return _UncheckedItems ?? (_UncheckedItems = GetAllUncheckedItems()); }
}
private ObservableCollection<ItemViewModel> GetAllUncheckedItems()
{
var toRet = new ObservableCollection<ItemViewModel>();
foreach (var i in Enumerable.Range(1,10))
{
toRet.Add(new ItemViewModel {Name = string.Format("Name-{0}", i), IsChecked = false});
}
return toRet;
}
#endregion
#region CheckedItems
private ObservableCollection<ItemViewModel> _CheckedItems;
public ObservableCollection<ItemViewModel> CheckedItems
{
get { return _CheckedItems ?? (_CheckedItems = GetAllCheckedItems()); }
}
private ObservableCollection<ItemViewModel> GetAllCheckedItems()
{
var toRet = new ObservableCollection<ItemViewModel>();
foreach (var i in Enumerable.Range(11, 20))
{
toRet.Add(new ItemViewModel { Name = string.Format("Name-{0}", i), IsChecked = true });
}
return toRet;
}
#endregion
And the command:
#region CheckItem
private RelayCommand<ItemViewModel> _CheckItemCommand;
public RelayCommand<ItemViewModel> CheckItemCommand
{
get { return _CheckItemCommand ?? (_CheckItemCommand = new RelayCommand<ItemViewModel>(ExecuteCheckItemCommand, CanExecuteCheckItemCommand)); }
}
private void ExecuteCheckItemCommand(ItemViewModel item)
{
//ComandCode
item.IsChecked = true;
UncheckedItems.Remove(item);
CheckedItems.Add(item);
}
private bool CanExecuteCheckItemCommand(ItemViewModel item)
{
return true;
}
#endregion
The magic here could be in the Data binding, in this case I used command parameter and the FindAncestor binding, check the Data Template:
<DataTemplate x:Key="UncheckedItemDataTemplate">
<Grid>
<StackPanel Orientation="Horizontal">
<TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="{Binding Name}" VerticalAlignment="Top"/>
<CheckBox HorizontalAlignment="Left" VerticalAlignment="Top" IsChecked="{Binding IsChecked}" IsEnabled="False"/>
<Button Content="Check" Width="75" Command="{Binding DataContext.CheckItemCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainWindow}}}" CommandParameter="{Binding Mode=OneWay}"/>
</StackPanel>
</Grid>
</DataTemplate>
<DataTemplate x:Key="CheckedItemDataTemplate">
<Grid>
<StackPanel Orientation="Horizontal">
<TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="{Binding Name}" VerticalAlignment="Top"/>
<CheckBox HorizontalAlignment="Left" VerticalAlignment="Top" IsChecked="{Binding IsChecked}" IsEnabled="False"/>
</StackPanel>
</Grid>
</DataTemplate>
One data template for checked items, and another for unchecked items. Now the usage, this is simpler:
<ListBox Grid.Row="2" Margin="5" ItemsSource="{Binding UncheckedItems}" ItemTemplate="{DynamicResource UncheckedItemDataTemplate}"/>
<ListBox Grid.Row="2" Margin="5" Grid.Column="1" ItemsSource="{Binding CheckedItems}" ItemTemplate="{DynamicResource CheckedItemDataTemplate}"/>
This is a cleaner solution, hope is helps.
Have xaml.cs file containing my ObservableCollection of my ViewModel. I have now implemented a command binding to a button click which invokes my function inside the viewmodel. The problem is that I do not get the item of my list in my button click function
xaml
<ItemsControl ItemsSource="{Binding ConditionList}" AlternationCount="{Binding ConditionList.Count}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<WrapPanel>
<Button Content="{Binding}" Command="{Binding DataContext.DeleteCondition,
RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" CommandParameter="{Binding}" />
</WrapPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Please note my button is in ItemControl
VM
private void DoDeleteCondition(object parameter)
{
// if (parameter != null)
// ...
}
public ICommand DeleteCondition
{
get
{
if (_DeleteCondition == null)
_DeleteCondition = new RelayCommand(o => DoDeleteCondition(o));
return _DeleteCondition;
}
}
You need to create a RelayCommand<T> where T is the Item in the ConditionList. Then you will get your parameter in the execute method.
I have a feeling that your binding is set a little backwards.
In your ItemsControl do you want to have:
the items from your collection and one command that will execute when you click on the single item
or the list of possible commands you want to execute on a single item that you have elsewhere (meaning the collection is displayed on some parent element, so you can bind to the single item somehow)?
... or maybe you have a separate command defined for every item in your collection ...? (then, how are the elements in your collection implemented?)
Depending on your answer:
1:
<ItemsControl ItemsSource="{Binding Path=MyObservableCollection}" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<WrapPanel>
<Button Content="{Binding}"
Command="{Binding Path=DataContext.DeleteCondition, RelativeSource={RelativeSource AncestorType=AncestorWithYourViewModelAsDataContext}}"
CommandParameter="{Binding}" />
</WrapPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
2:
<ItemsControl ItemsSource="{Binding Path=ConditionList}" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<WrapPanel>
<Button Content="{Binding}"
Command="{Binding Path=MyConditionalCommand}"
CommandParameter="{BindingToTheElementOfYourCllectionThatYouWantToActUpon}" />
</WrapPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
example implementation in your ViewModel:
private List<ConditionalCommand> _ConditionList;
public List<ConditionalCommand> ConditionList
{
get { return _ConditionList; }
set
{
if (_ConditionList != value)
{
_ConditionList = value;
OnPropertyChanged("ConditionList");
}
}
}
...
class ConditionalCommand
{
public ICommand MyConditionalCommand { get; set; }
public string Name { get; set; }
public override string ToString()
{
return Name;
}
}
...
this.ConditionList = new List<ConditionalCommand>();
this.ConditionList.Add(new ConditionalCommand{ MyConditionalCommand = DeleteCondition , Name="Delete"});
this.ConditionList.Add(new ConditionalCommand{ MyConditionalCommand = DeleteSpecial, Name="Delete special" });
....
private void DoDeleteCondition(object parameter)
{
// if (parameter != null)
// ...
}
public ICommand DeleteCondition
{
get
{
if (_DeleteCondition == null)
_DeleteCondition = new RelayCommand(o => DoDeleteCondition(o));
return _DeleteCondition;
}
}
// DeleteSpecial implemented in similar manner...
I have a type called "MyType" and my Pivot's ItemsSource is bound to an ObservableCollection property called "DataSource" inside the "myFirstVM" ViewModel. Inside "MyType" i have the property Title. As you can see from my XAML the TextBlock is bound to MyProperty. How to make it return the current item Title?
So for example, If i am on the second PivotItem, I need the Title of the second item in the DataSource collection
XAML:
<Grid x:Name="LayoutRoot">
<Pivot Name="myPivot"
SelectedItem="{Binding myFirstVM.SelItem, Mode=TwoWay}"
ItemsSource="{Binding myFirstVM.DataSource}"
ItemTemplate="{Binding myFirstVM.OtherTemplate}">
<Pivot.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding DataContext.myFirstVM.MyProperty, ElementName=myPivot}"/>
</DataTemplate>
</Pivot.HeaderTemplate>
</Pivot>
</Grid>
myFirstVM code:
private ObservableCollection<MyType> _dataSource;
public ObservableCollection<MyType> DataSource
{
get
{
if (this._dataSource == null)
{
this._dataSource = new ObservableCollection<MyType>();
}
return this._dataSource;
}
set { }
}
public string MyProperty
{
get
{
if (null != this.SelItem)
{
return this.SelItem.Title;
}
return "no title";
}
set { }
}
private MyType _selItem;
public MyType SelItem
{
get
{
return _selItem;
}
set
{
_selItem = value;
RaisePropertyChanged("SelItem");
RaisePropertyChanged("MyProperty");
}
}
public ObservableCollection<MyOtherType> OtherDataSource
{
get
{
if (null != this.SelItem)
{
return this.SelItem.OtherCollection;
}
else
{
return new ObservableCollection<MyOtherType>();
}
}
set { }
}
private MyOtherType _selOtherItem;
public MyOtherType SelOtherItem
{
get
{
return _selSegment;
}
set
{
_selSegment = value;
RaisePropertyChanged("SelOtherItem");
RaisePropertyChanged("PartsDataSource");
}
}
public ObservableCollection<MyThirdType> ThirdDataSource
{
get
{
if (null != this.SelOtherItem)
{
return this.SelOtherItem.ThirdCollection;
}
else
{
return new ObservableCollection<MyThirdType>();
}
}
set { }
}
And these are my DataTemplates for the inner collections "OtherDataSource" and "ThirdDataSource", that are ListBoxes:
<DataTemplate x:Key="OtherTemplate">
<ListBox DataContext="{Binding Source={StaticResource Locator}}"
ItemsSource="{Binding myFirstVM.OtherDataSource}"
ItemTemplate="{StaticResource ThirdTemplate}"
SelectedItem="{Binding myFirstVM.SelOtherItem, Mode=TwoWay}">
</ListBox>
</DataTemplate>
<DataTemplate x:Key="ThirdTemplate">
<ListBox DataContext="{Binding Source={StaticResource Locator}}"
ItemsSource="{Binding myFirstVM.ThirdDataSource}"
ItemTemplate="{StaticResource FourthTemplate}">
</ListBox>
</DataTemplate>
EDIT: I updated the question with the full ViewModel, and the DataTemplates, as sugested by #olitee. The problem with this approach as you can see is that in the second, and third dataTemplate I have ListBoxes. I am using one ViewModel for all the things. Any ideas?
You need to do a little extra work. Your ViewModel is not currently aware of which item is selected. You could create a new property called 'SelectedItem', and bind the Pivots' SelectedItem value.
Then you can access the Selected Item in code.
<Grid x:Name="LayoutRoot">
<Pivot Name="myPivot"
Tag="{Binding}"
SelectedItem="{Binding myFirstVM.SelectedItem}"
ItemsSource="{Binding myFirstVM.DataSource}"
ItemTemplate="{Binding myFirstVM.ViewDataTemplate}">
<Pivot.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding DataContext.myFirstVM.MyProperty, ElementName=myPivot}"/>
</DataTemplate>
</Pivot.HeaderTemplate>
</Pivot>
</Grid>
Then your VM would look something like:
private ObservableCollection<MyType> _dataSource;
public ObservableCollection<MyType> DataSource
{
get
{
if (this._dataSource == null)
{
this._dataSource = new ObservableCollection<MyType>();
}
return this._dataSource;
}
set { }
}
public string MyProperty
{
get
{
if (this.SelectedItem != null)
{
return this.SelectedItem.Title;
}
else
{
return null;
}
}
}
private MyType _selectedItem;
public MyType SelectedItem
{
get
{
return _selectedItem;
}
set
{
_selectedItem = value;
OnNotifyPropertyChanged("SelectedItem");
OnNotifyPropertyChanged("MyProperty");
}
}
Alternatively, if you're just wanting to fix up the text for presentation, and don't really require the SelectedItem in your VM, you could go with #Jehof's approach - but implement an IValueConvertor that performs the fix.
This should do the trick
<TextBlock Text="{Binding SelectedItem.Title, ElementName=myPivot}"/>
Bind the Text-Property to the SelectedItem property of the Pivot element. When the selected item of the Pivot changes the TextBlock should display the Title of the item.
This is the right XAML implementation:
SelectedItem="{Binding myFirstVM.SelectedItem, Mode=TwoWay}"
and in the code behind instead of OnNotifyPropertyChanged the ViewModel needs to inherit ViewModelBase, part of MVVM Light then in the setter of SelectedItem property:
RaisePropertyChanged("SelectedItem");