WPF Ordering DataBinding in nested user controls - c#

I have two user controls: 'Flags' and 'FlagOption'. 'FlagOption' is nested in an ItemsControl in the 'Flags' control like so:
Flags Control:
<ItemsControl ItemsSource="Binding RelativeSource={RelativeSource AncestorType=local:Flags}, Path=FlagOptions}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:FlagOption FlagValue="{Binding}" Field="{Binding RelativeSource={RelativeSource AncestorType=local:Flags}, Path=Field}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
CodeBehind:
public static readonly DependencyProperty FieldProperty = DependencyProperty.Register(nameof(Field), typeof(TagInterface.LongFlags), typeof(Flags));
public TagInterface.LongFlags Field
{
get { return (TagInterface.LongFlags)GetValue(FieldProperty); }
set { SetValue(FieldProperty, value); }
}
public IEnumerable<Enum> FlagOptions
{
get
{
return Enum.GetValues(Field.FieldValue.GetType()).Cast<Enum>();
}
}
FlagOption Control:
<CheckBox FlowDirection="LeftToRight" IsChecked="{Binding Path=IsChecked, RelativeSource={RelativeSource AncestorType=local:FlagOption}}"/>
CodeBehind:
public static readonly DependencyProperty FlagValueProperty = DependencyProperty.Register(nameof(FlagValue), typeof(Enum), typeof(FlagOption));
public static readonly DependencyProperty FieldProperty = DependencyProperty.Register(nameof(Field), typeof(TagInterface.LongFlags), typeof(FlagOption));
public Enum FlagValue
{
get { return (Enum)GetValue(FlagValueProperty); }
set { SetValue(FlagValueProperty, value); }
}
public TagInterface.LongFlags Field
{
get { return (TagInterface.LongFlags)GetValue(FieldProperty); }
set { SetValue(FieldProperty, value); }
}
public bool IsChecked
{
get
{
// Accessing Field Property here is null because it is bound AFTER IsChecked field
return false;
}
set
{
// TODO
}
}
The problem I'm having is that in the FlagOption control the "Field" Dependency Property is being bound AFTER the IsChecked Property, so the Field property is null in the Getter of the IsChecked property. If debug and step over the error the "Field" Dependency property eventually gets bound, but it's too late.
Can I control the order of the binding somehow? Why isn't the Field Dependency Property binding before my IsChecked Property?

Related

SelectedValue binding of a nested DataGrid doesn't work while ItemsSource does

I have a DataGrid inside of a UserControl which in turn lies inside of another UserControl. This is due to other needs of the project and I can't change this nested architecture. I'm binding a list of Person class to this DataGrid. This is a dumbed-down version without using a VM, but in my real project I am using a VM.
My UserControl with the DataGrid:
<Grid>
<DataGrid x:Name="MyDg"
ItemsSource="{Binding ItemsSource, RelativeSource={RelativeSource AncestorType=local:UCDataGrid}, UpdateSourceTrigger=PropertyChanged}"
MouseDoubleClick="MyDg_MouseDoubleClick"
SelectedValue="{Binding SelectedValue, RelativeSource={RelativeSource AncestorType=local:UCDataGrid}, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
Code Behind:
public partial class UCDataGrid : UserControl
{
public event RoutedEventHandler RoutedDataGridDoubleClick;
public UCDataGrid()
{
InitializeComponent();
}
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(object), typeof(UCDataGrid), new PropertyMetadata(null));
public object ItemsSource
{
get { return GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly DependencyProperty SelectedValueProperty = DependencyProperty.Register("SelectedValue", typeof(object), typeof(UCDataGrid), new PropertyMetadata(null));
public object SelectedValue
{
get { return GetValue(SelectedValueProperty); }
set { SetValue(SelectedValueProperty, value); }
}
private void MyDg_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
RoutedDataGridDoubleClick?.Invoke(this, new RoutedEventArgs());
}
}
2nd UserControl that contains the above control:
<Grid>
<ContentControl Content="{Binding MyDataGrid, ElementName=ucDisplay}"/>
</Grid>
ucDisplay is simply the Name property value of this UserControl.
Code Behind:
Nothing fancy here.
public partial class UCDisplay : UserControl
{
public UCDisplay()
{
InitializeComponent();
}
public static readonly DependencyProperty MyDataGridProperty = DependencyProperty.Register("MyDataGrid", typeof(object), typeof(UCDisplay), new PropertyMetadata(null));
public object MyDataGrid
{
get { return GetValue(MyDataGridProperty); }
set { SetValue(MyDataGridProperty, value); }
}
}
Main Window
In my Main Window, I bind my People list as well as SelectedPerson instance, like so:
<Grid>
<local:UCDisplay>
<local:UCDisplay.MyDataGrid>
<local:UCDataGrid ItemsSource="{Binding People}"
SelectedValue="{Binding SelectedPerson, UpdateSourceTrigger=PropertyChanged}"
RoutedDataGridDoubleClick="UCDataGrid_RoutedDataGridDoubleClick"/>
</local:UCDisplay.MyDataGrid>
</local:UCDisplay>
</Grid>
Code Behind:
public partial class MainWindow : Window, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
private List<Person> people;
public List<Person> People
{
get => people;
set => SetField(ref people, value);
}
private Person selectedPerson;
public Person SelectedPerson
{
get => selectedPerson;
set => SetField(ref selectedPerson, value);
}
public MainWindow()
{
InitializeComponent();
People = GetPeople();
DataContext = this;
}
private void UCDataGrid_RoutedDataGridDoubleClick(object sender, RoutedEventArgs e)
{
}
private List<Person> GetPeople()
{
return new List<Person>
{
new Person() { Name = "A" },
new Person() { Name = "B" },
new Person() { Name = "C" }
};
}
public class Person
{
public string Name { get; set; }
}
}
Again, in reality I'm using a VM, this is only to keep things simple.
Now when I run this I can display my list content just fine. But when I double-click an item in my DataGrid, in the corresponding in my Main Window code behind, the SelectedPerson remains null, although its binding is identical to the People list. I confirm this by using a break point in the main code behind:
But if I debug and see the value in the code behind of my innermost UserControl, you see that the SelectedValue there has the correct selected items value.
So what am I doing wrong here? Why can't I seem to bind the SelectedValue although I do it exactly the same as my ItemsSource binding, but the latter works?
SelectedValue is supposed to be used in conjunction with SelectedValuePath. You should use SelectedItem instead.
Besides that, you are missing a TwoWay Binding. Either explicitly declare the SelectedItem Binding TwoWay
<DataGrid x:Name="MyDg"
ItemsSource="{Binding ItemsSource,
RelativeSource={RelativeSource AncestorType=UserControl}}"
SelectedItem="{Binding SelectedItem,
RelativeSource={RelativeSource AncestorType=UserControl}, Mode=TwoWay}"/>
or register the property to bind TwoWay by default:
public static readonly DependencyProperty SelectedItemProperty =
DependencyProperty.Register(
nameof(SelectedItem), typeof(object), typeof(UCDataGrid),
new FrameworkPropertyMetadata(
null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public object SelectedItem
{
get { return GetValue(SelectedItemProperty); }
set { SetValue(SelectedItemProperty, value); }
}
Also note that setting UpdateSourceTrigger=PropertyChanged is pointless in all your Bindings.

Passing ModelData (context) between UserControls (views) MVVM Prism

I'm woking on a project and I have three ViewModels: ObjectDetailsViewMode has a Context (property linking to a model) of type ObjectBase; PropertyTextViewModel has a Context of type PropertyText and PropertyNumberViewModel has a Context of type PropertyNumber.
Below is the structure of the Models:
public class ObjectBase : ModelBase
{
private string _name;
public string Name
{
get { return _name; }
set { SetProperty(ref _name, value); }
}
public DataCollection<PropertyBase> Properties { get; } = new DataCollection<PropertyBase>();
}
public class PropertyText : PropertyBase
{
private string _default;
public string Default
{
get { return _default; }
set { SetProperty(ref _default, value); }
}
}
public class PropertyNumber : PropertyBase
{
private double _default = 0;
public double Default
{
get { return _default; }
set { SetProperty(ref _default, value); }
}
private double _minValue = 0;
public double MinValue
{
get { return _minValue; }
set { SetProperty(ref _minValue, value); }
}
private double _maxValue = 0;
public double MaxValue
{
get { return _maxValue; }
set { SetProperty(ref _maxValue, value); }
}
}
Regarding the views I have one for each ViewModel. The ObjectDetailsView is a use control that has a TextBox for editing the Object.Name, two buttons to add new PropertyText/PropertyNumber to the Object.Properties and an ItemsControl connected to that Object.Properties.
Each PropertyBase in the ItemsControl (ItemsSource) is resolved into a new view using the DataTemplate marker:
<ItemsControl ItemsSource="{Binding Object.Properties}">
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type models:PropertyText}">
<views:PropertyTextView />
</DataTemplate>
<DataTemplate DataType="{x:Type models:PropertyNumber}">
<views:PropertyNumberView />
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
As I'm using PRISM the correct ViewModel is automatically created for me and the view DataContext is then set to the new ViewModel. My problem is I need to pass the new Property from the Object.Properties list to the newly created View's ViewModel and store it in the Context property I have there.
I can't avoid creating a View/ViewModel for each property type because there is some under-the-hood logic on some Property types (not the ones I described here.. but I have other types like Boolean, Reference, Enum...)
So I really need to pass a value to the ViewModel I tried to use
<ItemsControl ItemsSource="{Binding Object.Properties}">
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type models:PropertyText}">
<views:PropertyTextView Context="{Binding}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type models:PropertyNumber}">
<views:PropertyNumberView Context="{Binding}"/>
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
Be aware that Context is a custom property I created inside the ViewModel's to store the ModelContext. I even created a DependencyProperty in the View's behind code:
public PropertyBase Context
{
get { return (PropertyBase)GetValue(ContextProperty); }
set { SetValue(ContextProperty, value); }
}
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ContextProperty =
DependencyProperty.Register("Context", typeof(PropertyBase), typeof(PropertyTextView), new PropertyMetadata(null));
But it doesn't get linked to the ViewModels set event (I made a break point there and... nothing). I even tried a SetBinding in the PropertyTextView code-behind (constructor):
string propertyInViewModel = "Context";
var bindingViewMode = new Binding(propertyInViewModel) { Mode = BindingMode.TwoWay };
this.SetBinding(ContextProperty, bindingViewMode);
No luck with any of these... I' really stuck.
Something More Simple
If the PropertyTextView has this dependency property.
public string Context
{
get { return (PropertyBase)GetValue(ContextProperty); }
set { SetValue(ContextProperty, value); }
}
// Using a DependencyProperty as the backing store for Context. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ContextProperty =
DependencyProperty.Register("Context", typeof(string), typeof(PropertyTextBuilderView), new PropertyMetadata(null));
I should be able to do:
right?! Why isn't the public property "Context" not being called (I placed a breakpoint there and I get nothing).
Instead of just setting the Context Property of your View to a new Binding you need to assign the Current DataContext like so:
<views:PropertyNumberView Context="{Binding .}"/>
This should assign the Current Views.DataContext Property to your new View.
If you're in an DataTemplate you probably need to specify the RelativeSource:
<views:PropertyNumberView Context="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType=UserControl}}
<ItemsControl ItemsSource="{Binding Object.Properties}">
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type models:PropertyText}">
<views:PropertyTextView Context="{Binding .}"/>
</DataTemplate>
<ItemsControl.Resources>
</ItemsControl>
As I'm using PRISM the correct ViewModel is automatically created for me
You don't have to use view-first with Prism. The ViewModelLocator is there to help, if you chose to, but view model-first is possible, too.
If I understand you correctly, you have a view model and want to populate a list with child view models. So do just that:
internal class ParentViewModel : BindableBase
{
public ParentViewModel( ParentModel parentModel, IChildViewModelFactory factory )
{
Children = new object[] { factory.CreateTextViewModel(parentModel.TextProperty), factory.CreateNumberViewModel(parentModel.NumberProperty) };
}
public IEnumerable Children { get; }
}
and map the different child view models to child views via DataTemplates.
parentModel.WhateverProperty will have a Name and Value properties as well as setter for the value, probably...

Dependency Property not set from ViewModel

I create a custom control "CustomAutoCompleteBox" (which inherit of AutoCompleteBox) with one dependency property "CurrentItem".
public static readonly DependencyProperty CurrentItemProperty =
DependencyProperty.Register("CurrentItem", typeof(CityEntity), typeof(CustomAutoCompleteBox),
new FrameworkPropertyMetadata(
null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public CityEntity CurrentItem
{
get { return (CityEntity)GetValue(CurrentItemProperty); }
set { SetValue(CurrentItemProperty, value); }
}
This custom control have also a property "InternalCurrentItem".
public CityEntity InternalCurrentItem
{
get { return _internalCurrentCity; }
set
{
if (_internalCurrentCity == value) return;
_internalCurrentCity = value;
OnPropertyChanged();
CurrentItem = value;
}
}
The DataContext is define to himself in the constructor :
public VilleAutoCompleteBox()
{
DataContext = this;
...
}
And the Style set ItemsSource and SelectedItem like this:
<Style TargetType="{x:Type infrastructure_controls:CustomAutoCompleteBox}" BasedOn="{StaticResource AutoCompleteBoxFormStyle}">
<Setter Property="ItemsSource" Value="{Binding InternalItems, Mode=OneWay}" />
<Setter Property="SelectedItem" Value="{Binding InternalCurrentItem, Mode=TwoWay}" />
...
</Style>
In summary, ItemsSource is bind to internal property "InternalItems" and SelectedItem is bind to internal property "InternalCurrentItem".
For use it, I declare this CustomAutoCompleteBox like this :
<infrastructure_usercontrols:CustomAutoCompleteBox Width="200" CurrentItem="{Binding DataContext.VmCurrentItem, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Mode=TwoWay}" />
I have bind the dependency property "CurrentItem" to the ViewModel's property "VmCurrentItem".
Everything works fine except for one thing.
When I type text in the control, the InternalCurrentItem property changes correctly. Same for the CurrentItem property in my ViewModel.
Concretely, InternalCurrentItem is correctly modified (Set). This property sets the CurrentItem dependency property, and this dependency property sets VmCurrentItem.
The opposite is not true. If I change directly the value of the VmCurrentItem property in the ViewModel, the CurrentItem property is not changed. I do not understand why.
The first case causes the following chain of events:
SelectedItem is changed
InternalCurrentItem is updated by the framework due to the binding
You manually update CurrentItem in the InternalCurrentItem setter
VmCurrentItem is updated by the framework due to the binding
In the opposite direction this is what happens:
VmCurrentItem is changed
CurrentItem is updated by the framework due to the binding
...and that's it. There's no binding and no piece of code that would update InternalCurrentItem when CurrentItem changes. So what you need to do is to register a PropertyChangedCallback for your CurrentItemProperty which will update InternalCurrentItem:
public static readonly DependencyProperty CurrentItemProperty =
DependencyProperty.Register(
"CurrentItem",
typeof(CityEntity),
typeof(CustomAutoCompleteBox),
new FrameworkPropertyMetadata
{
BindsTwoWayByDefault = true,
PropertyChangedCallback = CurrentItemPropertyChanged
});
private static void CurrentItemPropertyChanged(
DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = (CustomAutoCompleteBox)d;
control.InternalCurrentItem = (CityEntity)e.NewValue;
}
You need to declare the property the same way as the first:
public static readonly DependencyProperty InternalCurrentItemProperty =
DependencyProperty.Register("InternalCurrentItem", typeof(CityEntity), typeof(CustomAutoCompleteBox),
new FrameworkPropertyMetadata(
null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public CityEntity InternalCurrentItem
{
get{ return (CityEntity)GetValue(InternalCurrentItemProperty); }
set
{
SetValue(InternalCurrentItemProperty, value);
}
}

WPF ComboBox validation error when binding to IEnumerable of Enum values

For the purpose of code reuse, I am attempting to bind a ComboBox ItemsSource to an enumerable of enum values defined in a viewmodel. (I am aware of the strategies for binding directly to the enum, but in order to achieve code reuse I need to bind to an enumerable.) On viewmodel construction, I set the selected item to the first value of the enumerable. When the UI first launches, however, the combobox loads with validation error:
Value '' could not be converted.
This error does not occur when I use the same XAML to bind to an enumerable of classes. After I select an enum value, I get no more validation errors and the UI works as intended. How do I avoid this error and get the combobox to display the selected item on startup?
The code details... I have a service implementing IAcquire<T> which returns an enumerable of enum values:
public interface IAcquire<T>
{
IReactiveList<T> Items { get; }
}
My viewmodel inheritance looks something like this:
class GranularitySelectionViewModel : ChartFilterSelectionBase<DataGranularity>
{
public GranularitySelectionViewModel([NotNull] IAcquire<DataGranularity> service)
: base(service, "Granularity")
{}
}
class ChartFilterSelectionBase<T> : SelectionViewModelBase
{
private readonly IAcquire<T> _service;
internal ChartFilterSelectionBase([NotNull] IAcquire<T> service, string label)
:base(label)
{
foreach (var value in service.Items)
{
Items.Add(value);
}
SelectedItem = Items.FirstOrDefault();
}
private readonly IReactiveList<T> _items = new ReactiveList<T>();
public new IReactiveList<T> Items
{
get { return _items; }
}
private T _selectedItem;
public new T SelectedItem
{
get { return _selectedItem; }
set { SetProperty(ref _selectedItem, value); }
}
}
public class SelectionBaseViewModel
{
protected SelectionBaseViewModel([NotNull] string label )
{
if (label == null) throw new ArgumentNullException("label");
_label = label;
}
private readonly string _label;
public string Label
{
get { return _label; }
}
//Placeholder to be overridden in derived class.
public object SelectedItem { get; set; }
//Placeholder to be overridden in derived class.
public IReactiveList<object> Items { get; private set; }
}
The XAML is as follows:
<DataTemplate DataType="{x:Type viewModels:SelectionBaseViewModel}">
<StackPanel Orientation="Vertical">
<Label Content="{Binding Label}" ContentStringFormat="{}{0}:" Margin="5,5,5,0"/>
<ComboBox Margin="5,0,5,5" ItemsSource="{Binding Items, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" IsSynchronizedWithCurrentItem="True"
SelectedItem="{Binding SelectedItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" BorderThickness="1" BorderBrush="White">
</ComboBox>
</StackPanel>
</DataTemplate>

Usercontrol as datatemplate with bindings

I have an ItemsControl with ItemsSource binded to a list of SystemModels. It has to generate an usercontrol for every system in the list. And in those usercontrols it has some textboxes that show the name, is and location of the system.
My code creates the usercontrols but doesn't fill the textboxes in the usercontrol.
View:
<UserControl x:Name="SystemListScreen">
<ScrollViewer Grid.Row="1">
<ItemsControl x:Name="SystemList" ItemsSource="{Binding Path=Systems}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="4"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="SystemModel">
<Widgets:MultiLineButton partID="{Binding ID}"
company="{Binding ItemsSource.Company}"
typeSorter="{Binding ItemsSource.Name, ElementName=SystemList}"
typeLocation="{Binding ItemsSource.Location, ElementName=SystemList}"
buttonCommand="{Binding DataContext.navigateInspectList, ElementName=SystemListScreen}"
buttonCommandParameter="{Binding ItemsSource.ID, ElementName=SystemList}"/>
<!--<Button Content="{Binding ID}"/>-->
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</UserControl>
ViewModel:
private List<SystemModel> systems;
public SystemListViewModel()
{
Systems = new List<SystemModel>();
Systems = SystemAPI.Instance.GetSystems();
}
public string App { get; set; }
public List<SystemModel> Systems
{
get { return systems; }
private set
{
systems = value;
NotifyChanged("systems");
NotifyChanged("NoResultsFound");
}
}
multilinebutton code:
public static readonly DependencyProperty buttonCommandProperty = DependencyProperty.Register("buttonCommand", typeof(ICommand), typeof(MultiLineButton));
public static readonly DependencyProperty buttonCommandParameterProperty = DependencyProperty.Register("buttonCommandParameter", typeof(Object), typeof(MultiLineButton));
public static readonly DependencyProperty partIDProperty = DependencyProperty.Register("partID", typeof(String), typeof(MultiLineButton));
public static readonly DependencyProperty companyProperty = DependencyProperty.Register("company", typeof(String), typeof(MultiLineButton));
public static readonly DependencyProperty typeSorterProperty = DependencyProperty.Register("typeSorter", typeof(String), typeof(MultiLineButton));
public static readonly DependencyProperty typeLocationProperty = DependencyProperty.Register("typeLocation", typeof(String), typeof(MultiLineButton));
public MultiLineButton()
{
this.DataContext = this;
InitializeComponent();
}
public String partID
{
get { return (String)GetValue(partIDProperty); }
set { SetValue(partIDProperty, value); }
}
public String company
{
get { return (String)GetValue(companyProperty); }
set { SetValue(companyProperty, value); }
}
public String typeSorter
{
get { return (String)GetValue(typeSorterProperty); }
set { SetValue(typeSorterProperty, value); }
}
public String typeLocation
{
get { return (String)GetValue(typeLocationProperty); }
set { SetValue(typeLocationProperty, value); }
}
public ICommand buttonCommand
{
get { return (ICommand)GetValue(buttonCommandProperty); }
set { SetValue(buttonCommandProperty, value); }
}
public Object buttonCommandParameter
{
get { return (Object)GetValue(buttonCommandParameterProperty); }
set { SetValue(buttonCommandParameterProperty, value); }
}
what not works: partID="{Binding ID}", company="{Binding ItemsSource.Company}", typeSorter="{Binding ItemsSource.Name, ElementName=SystemList}", typeLocation="{Binding ItemsSource.Location, ElementName=SystemList}" and buttonCommandParameter="{Binding ItemsSource.ID, ElementName=SystemList}".
But if i use just a button as datatemplate with Content="{Binding ID}" It works Perfect, an d if i use the usercontrol outside the datatemplate it works also. But it will not work inside the datatemplate.
The error i get is this: "BindingExpression path error: 'Company' property not found on 'object' ''MultiLineButton' (Name='')'. BindingExpression:Path=Company; DataItem='MultiLineButton' (Name=''); target element is 'MultiLineButton' (Name=''); target property is 'company' (type 'String')"
How can i fix those bindings?
not sure, maybe you should remove DataType="SystemModel" from DateTemplate.
Or try to use simple textbox(Binding id) as DataTemplate, see if there is still empty.
If above didn't get you any help, try Snoop(snoopwpf.codeplex.com) see what happended.
try ObservableCollection:
private ObservableCollection<SystemModel> _systems = new ObservableCollection<SystemModel>();
public ObservableCollection<SystemModel> Systems { get { return _systems; } }
public SystemListViewModel()
{
var systems = SystemAPI.Instance.GetSystems();
foreach (var system in systems)
{
Systems.Add(system);
}
}
and xaml should be:
<UserControl x:Name="SystemListScreen">
<ScrollViewer Grid.Row="1">
<ItemsControl x:Name="SystemList" ItemsSource="{Binding Path=Systems}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="4"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Widgets:MultiLineButton
partID="{Binding ID}"
company="{Binding Company}"
typeSorter="{Binding Name}"
typeLocation="{Binding Location}"
buttonCommand="{Binding DataContext.navigateInspectList,
ElementName=SystemListScreen}"
buttonCommandParameter="{Binding ItemsSource.ID,
ElementName=SystemList}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</UserControl>
As chao wang said:
remove DataType="SystemModel" because if you're using just one type of DataType as DataTemplate it's not necessary. and the correct syntax is DataType="vm:SystemModel" where vm is defined in a parent tag like : xmlns:vm="clr-namespace:MySolution.MyVmProject.MyFolder"
also, check these:
remove ItemsSource. from Bindings inside DataTemplate because it's just wrong.
double check all names in bindings because if they're wrong, a null value is considered during runtime and you never know.
check your dataContext make sure UserControl have its DataContext to the correct instance of type dependency object which has Systems in it. and make sure it remains that way.

Categories