I'm using Caliburn.Micro for WPF (using VS 2012 and targeting to .NET 4.5.1).
I have problem with binding itemsSource to ComboBox (but I investigate that in my case it happens also with other controls with ItemsSource property, like ListBox).
I have nested views (usercontrols) with viewmodels created with SimpleContainer (IoC).
Here is my problem:
Combobox is populated with items not from its view viewmodel (LanguageSelectionViewModel) but from parent view viewmodel (TopViewModel).
Also, when I removed items collection from parent viewmodel, my combobox was empty.
Code:
MainWindowView.xaml:
<Window
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="300"
d:DataContext="{d:DesignInstance d:Type=mainWindow:MainWindowViewModel}"
>
<Grid>
<top:TopView
HorizontalAlignment="Stretch"
cal:Bind.Model="{Binding TopVM}"
/>
</Grid>
</Window>
MainWindowViewModel:
public class MainWindowViewModel : Screen
{
private TopViewModel topVm;
public TopViewModel TopVM
{
get { return topVm; }
set
{
topVm = value;
NotifyOfPropertyChange(() => TopVM);
}
}
public MainWindowViewModel(TopViewModel topVm, ContentViewModel contentVm)
{
TopVM = topVm;
TopVM.ConductWith(this);
}
}
TopView.xaml:
<UserControl>
<StackPanel Orientation="Horizontal">
<languageSelection:LanguageSelectionView cal:Bind.Model="{Binding LanguageSelectionVM}"/>
</StackPanel>
</UserControl>
TopViewModel.cs:
public class TopViewModel : Screen
{
private LanguageSelectionViewModel _languageSelectionVM;
public LanguageSelectionViewModel LanguageSelectionVM
{
get { return _languageSelectionVM; }
set
{
_languageSelectionVM = value;
NotifyOfPropertyChange(() => LanguageSelectionVM);
}
}
public TopViewModel(ClockViewModel clockVm, LanguageSelectionViewModel languageSelectionVM)
{
this.Items = new ObservableCollection<string>() { "a", "a", "a" };
LanguageSelectionVM = languageSelectionVM;
LanguageSelectionVM.ConductWith(this);
}
private ObservableCollection<string> _items;
public ObservableCollection<string> Items
{
get { return _items; }
set
{
_items = value;
NotifyOfPropertyChange(() => Items);
}
}
}
LanguageSelectionView.xaml:
<UserControl>
<StackPanel Orientation="Vertical">
<ComboBox ItemsSource="{Binding Items}"/>
</StackPanel>
</UserControl>
LanguageSelectionViewModel.cs:
public class LanguageSelectionViewModel : Screen
{
private ObservableCollection<string> _items;
public ObservableCollection<string> Items
{
get { return _items; }
set
{
_items = value;
NotifyOfPropertyChange(() => Items);
}
}
public LanguageSelectionViewModel()
{
this.Items = new ObservableCollection<string>() { "1", "a" };
}
}
I had also tried to populate this collection later, with no success:
protected override void OnViewReady(object view)
{
base.OnViewReady(view);
this.Items = new ObservableCollection<string>() { "1", "a" };
Refresh();
}
DataContext seems to be okay, because binding to textbox
<TextBlock Text="{Binding TestString}"/>
works fine.
Ok, mystery solved.
Instead of nesting controls like this:
<Grid>
<top:TopView
cal:Bind.Model="{Binding TopVM}" />
</Grid>
I should write:
<Grid>
<ContentControl
cal:View.Model="{Binding TopVM}" />
</Grid>
And there is no need to force DataContext.
I figure out that ComboBox whas the only control that had DataContext set to parent View Model, not to proper View model.
It works by forcing it in this way:
<ComboBox
DataContext="{Binding}"
ItemsSource="{Binding Items}" >
But still is the question - why? This is bug or feature of Caliburn.Micro?
Related
This app is displaying the class name of a collection instead of a text-box as desired. I've read other issues with this, but cannot figure out what I'm missing. I have a datacontext, I'm bound to the collection as an itemsource, and I've added a single item. All I want is to bind the collection 'Boxes' in my view model 'DrawBoxViewModel' to an item source, and have it display a single item as a text box. All help is appreciated.
First my XAML:
<Page
x:Class="BoxMaker2.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:BoxMaker2"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="using:BoxMaker2.ViewModels"
mc:Ignorable="d">
<Page.Resources>
<vm:DrawBoxViewModel x:Key="DrawBoxViewModel"/>
</Page.Resources>
<Canvas DataContext="{Binding Source={StaticResource DrawBoxViewModel}}">
<ItemsControl ItemsSource="{Binding Boxes}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Width="350" Height="600" Background="AliceBlue"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.Resources>
<DataTemplate x:DataType="vm:Box" x:Key="test">
<VariableSizedWrapGrid>
<TextBox Background="White"
Text="{x:Bind Data}"
Width="100"
Height="100"/>
<VariableSizedWrapGrid.RenderTransform>
<TranslateTransform X="{Binding LeftCanvas}" Y="{Binding TopCanvas}"/>
</VariableSizedWrapGrid.RenderTransform>
</VariableSizedWrapGrid>
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
</Canvas>
And now my viewmodel:
namespace BoxMaker2.ViewModels
{
public class DrawBoxViewModel
{
#region fields
private ObservableCollection<Box> _boxes;
#endregion
#region properties
public ObservableCollection<Box> Boxes { get { return this._boxes; } }
#endregion
#region constructors
public DrawBoxViewModel()
{
this._boxes = new ObservableCollection<Box>();
_boxes.Add(new Box() { Data = "hello!", LeftCanvas = 200, TopCanvas = 200 });
}
#endregion
}
public class Box : INotifyPropertyChanged
{
private int _generation;
public int Generation
{
get { return _generation; }
set { _generation = value; OnPropertyChanged("Generation"); }
}
private int _childNo;
public int ChildNo
{
get { return _childNo; }
set { _childNo = value; OnPropertyChanged("ChildNo"); }
}
private Box _parentBox;
public Box ParentBox
{
get { return _parentBox; }
set { _parentBox = value; OnPropertyChanged("ParentBox"); }
}
private List<Box> _childrenBox;
public List<Box> ChildrenBox
{
get { return _childrenBox; }
set { _childrenBox = value; OnPropertyChanged("ChildrenBox"); }
}
private string _data;
public string Data
{
get { return _data; }
set
{
_data = value;
OnPropertyChanged("Data");
}
}
private double _topCanvas;
public double TopCanvas
{
get { return _topCanvas; }
set
{
_topCanvas = value;
OnPropertyChanged("TopCanvas");
}
}
private double _leftCanvas;
public double LeftCanvas
{
get { return _leftCanvas; }
set
{
_leftCanvas = value;
OnPropertyChanged("LeftCanvas");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
I am not exactly sure what you are trying to achieve but here's a few issues I have found in your code.
You should assign your VM to the DataContext of the Page
directly.
<Page.DataContext>
<vm:DrawBoxViewModel />
</Page.DataContext>
After doing so, you can now remove DataContext="{Binding
Source={StaticResource DrawBoxViewModel}}" from your Canvas.
Replace <ItemsControl.Resource> with
<ItemsControl.ItemTemplate> and remove x:Key="test", assuming you want to show multiple
TextBoxes on the UI. The DataTemplate within the Resource you
defined won't do anything until you reference it by its key. I don't
think you really want that here though.
You should use x:Bind for your X & Y binding
<TranslateTransform X="{x:Bind LeftCanvas}"
Y="{x:Bind TopCanvas}" />
Your Boxes collection can be simplified as following
#region properties
public ObservableCollection<Box> Boxes { get; } = new ObservableCollection<Box>();
#endregion
#region constructors
public DrawBoxViewModel()
{
Boxes.Add(new Box() { Data = "hello!", LeftCanvas = 0, TopCanvas = 200 });
}
#endregion
Hope this helps!
Your Items control doesn't know which data template to use. Currently your view model has a template associated to it via the x:DataType="vm:Box" which is defined as a resource in the items control.
The problem is that Universal Windows Platform doesn't recognize templates associated to data types. So even though there is a template, the control doesn't know how to find it when it is rendering the collection of view models.
Automatic resolving of templates based on bound types was a function of WPF which is not available in UWP.
What that means is that in WPF you could associate a data template to a class/object via the x:DataType="Object Type" attribute of the data template (which is what you did). When the collection is bound, the rendering engine would auto-magically match the the individual items in the collection to their respective templates.
This was very powerful because if your collection had many different types of boxes for example (or things inheriting from DrawBoxViewModel) you could render each item type differently by simply defining a template. Well this is no more. Microsoft destroyed that feature in UWP.
So long story short - move the template to the page resource collection. Give it a key such as:
<Page.Resources>
<vm:DrawBoxViewModel x:Key="DrawBoxViewModel"/>
<DataTemplate x:Key="test">
<VariableSizedWrapGrid>
<TextBox Background="White"
Text="{x:Bind Data}"
Width="100"
Height="100"/>
<VariableSizedWrapGrid.RenderTransform>
<TranslateTransform X="{Binding LeftCanvas}" Y="{Binding TopCanvas}"/>
</VariableSizedWrapGrid.RenderTransform>
</VariableSizedWrapGrid>
</DataTemplate>
</Page.Resources>
Reference the template in your items control as follows:
<ItemsControl ItemsSource="{Binding Boxes} ItemTemplate={StaticResource test} ">
Preface
The control I am giving as an example is an sample work for a larger project. I have already had some help from the community on Stackoverflow ironing out some of the finer points of bindings within the control. The surprise has been that I am having an issue binding in the control's hosting form.
I have read and researched around DependencyProperty for a lot of hours. I was not a WPF developer at the start of the year but I am now covering the role because of a death in the business, and I accept this is a big hill to climb.
The question is what is missing here in my:
The hosting form's XAML code
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:AControl="clr-namespace:AControl;assembly=AControl" x:Class="DependencySampler.MainWindow"
Title="MainWindow" Height="350" Width="525">
<Grid>
<AControl:UserControl1 x:Name="cboBob" HorizontalAlignment="Left" Margin="100,118,0,0" VerticalAlignment="Top" Width="200" Height="29" SelectedColor="{Binding Path=BeSelected, Mode=OneWayToSource}"/>
</Grid>
The code behind
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new viewModelBinding();
BeSelected = new modelMain("Yellow", "#FFFFE0");
}
public modelMain BeSelected
{
get { return ((viewModelBinding)DataContext).Selected; }
set { ((viewModelBinding)DataContext).Selected = value; }
}
}
The ViewModel
public class viewModelBinding :ViewModelBase
{
modelMain sel = new modelMain("Red", "#FF0000");
public modelMain Selected
{
get { return sel; }
set { SetProperty(ref this.sel, value, "Selected"); }
}
}
The next section is the control itself.
The Model
public class modelMain:ViewModelBase
{
public modelMain(string colName, string hexval)
{
ColorName = colName;
HexValue = hexval;
}
string colorName;
public string ColorName
{
get { return colorName; }
set { SetProperty(ref this.colorName, value, "ColorName"); }
}
string hexValue;
public string HexValue
{
get { return hexValue; }
set { SetProperty(ref this.hexValue, value, "HexValue"); }
}
}
The ViewModel
public class viewModelMain:ViewModelBase
{
ObservableCollection<modelMain> val = new ObservableCollection<modelMain>();
public ObservableCollection<modelMain> ColorsList
{
get { return val; }
set { SetProperty(ref this.val, value, "Colors"); }
}
modelMain selectedColor;
public modelMain SelectedColour
{
get{return selectedColor;}
set { SetProperty(ref this.selectedColor, value, "SelectedColour"); }
}
public void SetCurrentColor(modelMain col)
{
SelectedColour = this.val.Where(x => x.ColorName == col.ColorName).FirstOrDefault();
}
public viewModelMain()
{
val.Add(new modelMain("Red", "#FF0000"));
val.Add(new modelMain("Blue", "#0000FF"));
val.Add(new modelMain("Green", "#008000"));
val.Add(new modelMain("Yellow", "#FFFFE0"));
SelectedColour = new modelMain("Blue", "#0000FF");
}
}
The UserControl XAML
<UserControl x:Class="AControl.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="32" d:DesignWidth="190">
<Grid>
<ComboBox x:Name="cboValue"
SelectionChanged="cboValue_SelectionChanged"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
ItemsSource="{Binding ColorList, RelativeSource={RelativeSource AncestorType=UserControl}}"
SelectedValue="{Binding SelectedColor, RelativeSource={RelativeSource AncestorType=UserControl}}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Width="10"
Height="10"
Margin="5"
Background="{Binding ColorName}"/>
<TextBlock Width="35"
Height="15"
Margin="5"
Text="{Binding ColorName}"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Grid>
The UserControl Code behind
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
ObservableCollection<modelMain> colorList = new viewModelMain().ColorsList;
public ObservableCollection<modelMain> ColorList
{
get { return colorList; }
set { colorList = value; }
}
public static readonly DependencyProperty SelectedColorProperty = DependencyProperty.Register(
"SelectedColor",
typeof(modelMain),
typeof(UserControl1),
new FrameworkPropertyMetadata(
null,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
new PropertyChangedCallback(OnSelectedColorChanged),
new CoerceValueCallback(CoerceSelectedColorCallback)));
private static void OnSelectedColorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
UserControl1 uc = (UserControl1)d;
uc.SelectedColor = (modelMain)e.NewValue;
}
private static object CoerceSelectedColorCallback(DependencyObject d, object value)
{
return (modelMain)value;
}
public modelMain SelectedColor
{
get { return (modelMain)GetValue(SelectedColorProperty); }
set { SetValue(SelectedColorProperty, value); }
}
private void cboValue_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var dat = sender as ComboBox;
SelectedColor = (modelMain)dat.SelectedValue;
}
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
//var dat = sender as ComboBox;
////SelectedColor = (modelMain)dat.SelectedValue;
//SelectedColor = (modelMain)this.SelectedColor;
}
}
Please note that in the code behind there is unused code but within the sample I have used then for placing break points
I understand that no DataContext should exist in the UserControl because it precludes one in the hosting form.
The Question
I was expecting the this line would be sufficient in the hosting form.
<AControl:UserControl1 x:Name="cboBob" HorizontalAlignment="Left" Margin="100,118,0,0" VerticalAlignment="Top" Width="200" Height="29" SelectedColor="{Binding Path=BeSelected, Mode=OneWayToSource}"/>
But it does not seem to do what I expected. I can see the BeSelected be initialised and it is holding a value but when the form loads I am expecting the colour yellow to enter the UserControl's and set DependencyProperty SelectedColor. This is not happening why and how can I get it to happen?
To get you example working, do the following (for the most part, implement what the commenters said):
The hosting form's XAML code
<AControl:UserControl1 x:Name="cboBob" HorizontalAlignment="Left" Margin="100,118,0,0" VerticalAlignment="Top" Width="200" Height="29" SelectedColor="{Binding Path=BeSelected, RelativeSource={RelativeSource AncestorType=Window}}}"/>
The Mode doesn't really matter since MainWindow doesn't implement INPC nor does it ever know when ((viewModelBinding)DataContext).Selected (and therefor, BeSelected) is changed. Actually, like Joe stated, OneWayToSource doesn't work... RelativeSource was needed because BeSelected is a property of the MainWindow - not MainWindow's DataContext.
modelMain
modelMain needs to implement IEquatable (like Janne commented). Why? Because BeSelected = new modelMain(...) creates a new modelMain which is not one of the items in the ComboBox's ItemsSource (ColorList). The new object may have the same property values as one of the items but that doesn't make them equal (different objects = different address in memory). IEquatable gives you the opportunity to override that.
public class modelMain : ViewModelBase, IEquatable<modelMain>
{
...
public bool Equals(modelMain other)
{
return (HexValue == other.HexValue);
}
}
viewModelMain's ColorList's setter is calling SetProperty with property name "Colors" when it should be "ColorsList". It's not being used so it doesn't stop your example from working but it's still wrong.
I'm trying to get the databinding I need to work with a ListBox.
I've parsed some data from a text file to a ObservableCollection<ViewModel> but the data isn't updating in the ListBox.
Here's some information:
The data which is written to from the parser:
class MainData
{
private static ObservableCollection<GroupViewModel> groupModelList = new ObservableCollection<GroupViewModel>();
public static ObservableCollection<GroupViewModel> GroupModelList
{
get { return groupModelList; }
}
}
What GroupViewModel holds (not everything but it's all the same):
class GroupViewModel : INotifyPropertyChanged
{
private GroupModel groupModel;
public event PropertyChangedEventHandler PropertyChanged;
public GroupViewModel()
{
groupModel = new GroupModel();
}
public string Name
{
get { return groupModel.name; }
set
{
if (groupModel.name != value)
{
groupModel.name = value;
InvokePropertyChanged("Name");
}
}
}
...
}
And what GroupModel Holds:
class GroupModel
{
public string name { get; set; }
}
This is how the parser adds new items to the GroupModelView:
if (split[0] == "group")
{
currentGroup = new GroupViewModel();
currentGroup.Name = split[1];
MainData.GroupModelList.Add(currentGroup);
}
I created a ListBox in my WPF application with these XAML options:
<Window x:Class="SoundManager.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:SoundManager.ViewModels"
xmlns:vm2="clr-namespace:SoundManager.Code"
Title="MainWindow" Height="720" Width="1280">
<Window.Resources>
<vm:MainViewModel x:Key="MainViewModel" />
<vm2:MainData x:Key="MainData" />
</Window.Resources>
<ListBox Grid.Row="2" Height="484" HorizontalAlignment="Left" Margin="12,0,0,0" Name="lbFoundItems" VerticalAlignment="Top" Width="201" ItemsSource="{Binding Source={StaticResource MainData}, Path=GroupModelList/Name}" />
but for some reason the data isn't updating in the UI (new items aren't added visibly in the UI).
I've been just getting started with the MVVM pattern and databinding and I can't figure out what I'm doing wrong.
Thanks in advance!
GroupModelList/Name is not a valid property path here. Setting it like that does not make the ListBox show the Name property of the data items in the GroupModelList collection.
You would instead have to set the ListBox's DisplayMemberPath property:
<ListBox ItemsSource="{Binding Source={StaticResource MainData}, Path=GroupModelList}"
DisplayMemberPath="Name"/>
or set the ItemTemplate property:
<ListBox ItemsSource="{Binding Source={StaticResource MainData}, Path=GroupModelList}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Moreover, the GroupModelList property should not be static:
class MainData
{
private ObservableCollection<GroupViewModel> groupModelList =
new ObservableCollection<GroupViewModel>();
public ObservableCollection<GroupViewModel> GroupModelList
{
get { return groupModelList; }
}
}
Then you might have MainData as a property in your view model, and bind the ListBox like this:
<ListBox ItemsSource="{Binding Source={StaticResource MainViewModel},
Path=MainData.GroupModelList}" .../>
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");
I have a problem to display the contents of a ReactiveList in a ListBoxcontrol in my view. When I try to bind it via code-behind bindings (using this.OneWayBind(...)) the list stays empty. I am using the latest ReactiveUI version (6.1.0). If I change the binding to XAML-Binding and remove the call to OneWayBind(...), the list will display the five String elements.
I don't know why this isn't working, a simple TextBlock.Text-Binding works as expected (see the code).
MainWindow.xaml:
<Window x:Class="View_Location_Test.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:local="clr-namespace:View_Location_Test"
xmlns:System="clr-namespace:System;assembly=mscorlib"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<ListBox x:Name="ToasterList">
<!-- This is working if you uncomment the above line and remove the OneWayBind call in the view-code: -->
<!--<ListBox x:Name="ToasterList" ItemsSource="{Binding ToasterList}">-->
<ListBox.Resources>
<DataTemplate DataType="{x:Type System:String}">
<TextBlock Text="{Binding}" />
</DataTemplate>
</ListBox.Resources>
</ListBox>
<TextBlock x:Name="ToasterName" />
</StackPanel>
MainWindow.xaml.cs:
public partial class MainWindow : Window, IViewFor<ViewModel>
{
public MainWindow()
{
ViewModel = new ViewModel();
DataContext = ViewModel;
InitializeComponent();
// bind
this.OneWayBind(ViewModel, vm => vm.ToasterList, v => v.ToasterList.ItemsSource);
this.Bind(ViewModel, vm => vm.Name, v => v.ToasterName.Text);
}
public ViewModel ViewModel
{
get { return (ViewModel)GetValue(ViewModelProperty); }
set { SetValue(ViewModelProperty, value); }
}
public static readonly DependencyProperty ViewModelProperty =
DependencyProperty.Register("ViewModel", typeof(ViewModel), typeof(MainWindow), new PropertyMetadata(null));
object IViewFor.ViewModel
{
get { return ViewModel; }
set { ViewModel = (ViewModel)value; }
}
}
ViewModel.cs:
public class ViewModel : ReactiveObject
{
public ViewModel()
{
ToasterList.Add("Toaster 1");
ToasterList.Add("Toaster 2");
ToasterList.Add("Toaster 3");
ToasterList.Add("Toaster 4");
ToasterList.Add("Toaster 5");
}
private String name = "My name";
public String Name
{
get { return name; }
set { this.RaiseAndSetIfChanged(ref name, value); }
}
private ReactiveList<String> toasterList = new ReactiveList<string>();
public ReactiveList<String> ToasterList
{
get { return toasterList; }
set { this.RaiseAndSetIfChanged(ref toasterList, value); }
}
}
This is because you've set the ItemsTemplate in a Weird Way™, so ReactiveUI thinks that you have no ItemsTemplate and is setting up the convention-based one (which is overkill for just a string).
Instead, set it like this:
<ListBox x:Name="ToasterList">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>