I have a binding problem in silverlight and I cannot make it work...
I have a control which has a model behind.
The control code behind looks like this
.....
public ClientListingVM()//constructor
{
ClientListingViewModel model = new ClientListingViewModel();//this is my model
DataContext = model;
InitializeComponent();
}
.....
the model code behind looks like this
....
public ObservableCollection<FilterItemDefinition> FilterItemDefinitions
{
get { return _filterItemDefinitions; }
set
{
if (_filterItemDefinitions != value)
{
_filterItemDefinitions = value;
}
OnPropertyChanged("FilterItems");
}
}
....
In the control xaml I have this
....
<my:Filters Height="150" FilterItems="{Binding Path=FilterItemDefinitions}" Columns="{Binding ElementName=ClientGrid, Path=Columns}"/>
....
where Filters is a user control...
Until here everything works ok...
The code behind for filters looks like this
....
public partial class Filters : UserControl
{
public Filters()
{
InitializeComponent();
}
private DependencyProperty FilterItemsProperty = DependencyProperty.Register("FilterItems", typeof(ObservableCollection<FilterItemDefinition>), typeof(Filters), new PropertyMetadata(null, new PropertyChangedCallback(OnChangef)));
public ObservableCollection<FilterItemDefinition> FilterItems
{
get
{
return (ObservableCollection<FilterItemDefinition>)GetValue(FilterItemsProperty);
}
set
{
SetValue(FilterItemsProperty, value);
}
}
private DependencyProperty ColumnsProperty = DependencyProperty.Register("Columns", typeof(ObservableCollection<DataGridColumn>), typeof(Filters), new PropertyMetadata(null, new PropertyChangedCallback(OnChangec)));
public ObservableCollection<DataGridColumn> Columns
{
get
{
return (ObservableCollection<DataGridColumn>)GetValue(ColumnsProperty);
}
set
{
SetValue(ColumnsProperty, value);
}
}
public static void OnChangef(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
public static void OnChangec(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
}
....
and the xaml looks like this
....
<ListBox x:Name="myListBox" ItemsSource="{Binding Path=FilterItems, Mode=TwoWay}" >
<ListBox.ItemTemplate>
<DataTemplate>
<my:FilterItem Columns="{Binding Source={StaticResource DataContextProxy},Path=DataSource.Columns}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
....
So here is the problem:
the ListBox ItemSource is bound to FilterItems... but the Filters DataContext is ClientListingViewModel... and that model does not have FilterItems property.
If in the Filters constructor I set a FiltersViewModel or I say DataContext=this the Filters DataContext will have the property FilterItems and the binding willwork ok.. But the property FilterItemDefinitions from ClientListingViewModel will not get to my control...
I would like that the DataContext for FiltersControl to be his DataContext or the code behind.. but in the same time the binding of his properties with the parent control model to work..
I am sure this is possible and I am doing it in the wrong way.. can someone please help me ...
10x
Leave your existing DataContext alone, you don't want to be doing things like DataContext = this. Instead bind ItemsSource like this:-
ItemsSource="{Binding Path=Parent.FilterItems, ElementName=LayoutRoot}"
This assumes you have default root element named as LayoutRoot. Hence the Parent of this root will be your Filters user control which has a FilterItems property. BTW Two way binding on this property doesn't make a lot of sense.
Related
I have a multiselect Combobox usercontrol and a dependency property 'SelectedItems'.
I m trying to use the usercontrol and bind the 'SelectedItems' to another property called 'SelectedResultItems' in my ViewModel. But I dont get any values to SelectedResultItems. Please help
Here is what i tried.
My main xaml:
<DataTemplate x:Key="TypeATemplate">
<control:MultiSelectComboBox Width="315" ItemsSource="{Binding
ResultvalueList,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
SelectedItems="{Binding
SelectedResultItems,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
My Combobox usercontrol code behind:
public static readonly DependencyProperty SelectedItemsProperty =
DependencyProperty.Register("SelectedItems",
typeof(ObservableCollection<string>), typeof(MultiSelectComboBox), new
FrameworkPropertyMetadata(null,new
PropertyChangedCallback(MultiSelectComboBox.OnSelectedItemsChanged)));
public ObservableCollection<string> SelectedItems
{
get { return
(ObservableCollection<string>)GetValue(SelectedItemsProperty); }
set
{
SetValue(SelectedItemsProperty, value);
}
}
I am setting the 'SelectedItems' on click of the checkbox.
My mainviewmodel:
public ObservableCollection<string> SelectedResultItems
{
get => _selectedResultItems;
set
{
_selectedResultItems = value;
NotifyPropertyChanged(nameof(SelectedResultItems));
}
}
If this is the same as for ListView(never used MultiSelectCombobox), you cannot bind to SelectedItems because it is a read-only property.
What I did to solve that is add the event SelectionChanged to ListView(or MultiSelectCombobox for you).
Then event would be :
private void YourComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
contexte.ResultItems = YourComboBox.SelectedItems.Cast<YourItem>().ToList();
}
Maybe there is a different way to do it, but until now that's the easiest way I found.
I'm developing a WPF application and I'm struggling a little bit to understand some of the details of DataContext as it applies to binding. My application uses a business object which is defined like this:
public class MyBusinessObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
{
PropertyChanged(this, e);
}
}
// enumerations for some properties
public enum MyEnumValues
{
[Description("New York")]
NewYork,
[Description("Chicago")]
Chicago,
[Description("Los Angeles")]
LosAngeles
}
// an example property
private string _myPropertyName;
public string MyPropertyName
{
get { return _myPropertyName; }
set
{
if (_myPropertyName == value)
{
return;
}
_myPropertyName = value;
OnPropertyChanged(new PropertyChangedEventArgs("MyPropertyName"));
}
}
// another example property
private MyEnumValues _myEnumPropertyName;
public MyEnumValues MyEnumPropertyName
{
get { return _myEnumPropertyName; }
set
{
if (_myEnumPropertyName== value)
{
return;
}
_myEnumPropertyName= value;
OnPropertyChanged(new PropertyChangedEventArgs("MyEnumPropertyName"));
}
}
// example list property of type Widget
public List<Widget> MyWidgets { get; set; }
// constructor
public MyBusinessObject()
{
// initialize list of widgets
MyWidgets = new List<Widget>();
// add 10 widgets to the list
for (int i = 1; i <= 10; i++)
{
MyWidgets.Add(new Widget());
}
// set default settings
this.MyPropertyName = string.empty;
}
}
As you can see, I have some properties that are declared in this class one of which is a list of Widgets. The Widget class itself also implements INotifyPropertyChanged and exposes about 30 properties.
My UI has a combobox which is bound to my list of Widgets like this:
MyBusinessObject myBusinessObject = new MyBusinessObject();
public MainWindow()
{
InitializeComponent();
this.DataContext = myBusinessObject;
selectedWidgetComboBox.ItemsSource = myBusinessObject.MyWidgets;
selectedWidgetComboBox.DisplayMemberPath = "WidgetName";
selectedWidgetComboBox.SelectedValuePath = "WidgetName";
}
The majority of the controls on my UI are used to display the properties of a Widget. When my user selects a Widget from the combobox, I want these controls to display the properties for the selected Widget. I'm currently achieving this behavior by updating my window's DataContext in the SelectionChanged event handler of my combobox like this:
private void selectedWidgetComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
this.DataContext = selectedWidgetComboBox.SelectedItem;
}
This allows me to bind my controls to the appropriate Widget property like this:
<TextBox Text="{Binding WidgetColor}"></TextBox>
However, not all of the controls in my UI are used to display Widget properties. Some of the controls need to display the properties from MyBusinessObject (for example: MyPropertyName defined above). In this scenario, I can't simply say:
<TextBox Text="{Binding MyPropertyName}"></TextBox>
...because the DataContext of the window is pointing to the selected Widget instead of MyBusinessObject. Can anyone tell me how I set the DataContext for a specific control (in XAML) to reference the fact that MyPropertyName is a property of MyBusinessObject? Thank you!
Instead of changing the DataContext of your window, you should add a property to your MyBusinessObject class like this one:
private Widget _selectedWidget;
public Widget SelectedWidget
{
get { return _selectedWidget; }
set
{
if (_selectedWidget == value)
{
return;
}
_selectedWidget = value;
OnPropertyChanged(new PropertyChangedEventArgs("SelectedWidget"));
}
}
Then bind SelectedWidget to the SelectedItem property of your combobox. Anywhere that you need to use the widget's properties you can do this:
<TextBox Text="{Binding Path=SelectedWidget.WidgetColor}"></TextBox>
try
<TextBox Text="{Binding MyBusinessObject.MyPropertyName}"></TextBox>
this works if MyBusinessObject is the datacontext of the textbox and MyPropertyName is a property of MyBusinessObject
Also, Here is a good article to clarify binding
hope this helps
EDIT 1:
use a relative binding like this:
text="{Binding DataContext.MyPropertyName, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TypeOfControl}}}"
So the relatve binding allows you to look up the visual tree to another UI element and use its datacontext. I would consider wrapping your window's contents in a grid. and wet your windows datacontext to the businessobject and the grids datacontext to the widget. That way you can always use the parent window's datacontext through the realtive source binding.
so use the following if your window's datacontext is your business object
text="{Binding DataContext.MyPropertyName, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"
I have an ObservableCollection of "Layouts" and a "SelectedLocation" DependencyProperty on a Window. The SelectedLocation has a property called "Layout", which is an object containing fields like "Name" etc. I'm trying to bind a combobox to the SelectedLayout but it's not working.
The following does not work, I've tried binding to SelectedItem instead to no avail. I believe it may be something to do with the fact that I'm binding to a subProperty of the SelectedLocation DependencyProperty (though this does implement INotifyPropertyChanged.
<ComboBox Grid.Row="2" Grid.Column="0" x:Name="cboLayout" ItemsSource="{Binding Layouts,ElementName=root}" SelectedValue="{Binding SelectedLocation.Layout.LayoutID,ElementName=root}" DisplayMemberPath="{Binding Name}" SelectedValuePath="LayoutID" />
However, the following works (Also bound to the "SelectedLocation" DP:
<TextBox Grid.Row="4" Grid.Column="1" x:Name="txtName" Text="{Binding SelectedLocation.Name,ElementName=root,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />
What type property Layouts has? I suppose something like this this: IEnumerable<Layout>.
But you bind selected value to Layout.LayoutID. So you got situation, when combo box contains Layout objects, and you try to select it by Int identifier. Of course binding engine can't find any Int there.
I have no idea about details of your code, so one thing I could propose: try to reduce your binding expression: SelectedItem="{Binding SelectedLocation.Layout,ElementName=root}.
If no success, provide more code to help me understand what's going on.
====UPDATE====
As I've said, you are obviously doing something wrong. But I am not paranormalist and couldn't guess the reason of your fail (without your code). If you don't want to share your code, I decided to provide simple example in order to demonstrate that everything works. Have a look at code shown below and tell me what is different in your application.
Class Layout which exposes property LayoutId:
public class Layout
{
public Layout(string id)
{
this.LayoutId = id;
}
public string LayoutId
{
get;
private set;
}
public override string ToString()
{
return string.Format("layout #{0}", this.LayoutId);
}
}
Class SelectionLocation which has nested property Layout:
public class SelectedLocation : INotifyPropertyChanged
{
private Layout _layout;
public Layout Layout
{
get
{
return this._layout;
}
set
{
this._layout = value;
this.OnPropertyChanged("Layout");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
var safeEvent = this.PropertyChanged;
if (safeEvent != null)
{
safeEvent(this, new PropertyChangedEventArgs(name));
}
}
}
And Window class with dependency properties (actually, in my example StartupView is UserControl, but it doesn't matter):
public partial class StartupView : UserControl
{
public StartupView()
{
InitializeComponent();
this.Layouts = new Layout[] { new Layout("AAA"), new Layout("BBB"), new Layout("CCC") };
this.SelectedLocation = new SelectedLocation();
this.SelectedLocation.Layout = this.Layouts.ElementAt(1);
}
public IEnumerable<Layout> Layouts
{
get
{
return (IEnumerable<Layout>)this.GetValue(StartupView.LayoutsProperty);
}
set
{
this.SetValue(StartupView.LayoutsProperty, value);
}
}
public static readonly DependencyProperty LayoutsProperty =
DependencyProperty.Register("Layouts",
typeof(IEnumerable<Layout>),
typeof(StartupView),
new FrameworkPropertyMetadata(null));
public SelectedLocation SelectedLocation
{
get
{
return (SelectedLocation)this.GetValue(StartupView.SelectedLocationProperty);
}
set
{
this.SetValue(StartupView.SelectedLocationProperty, value);
}
}
public static readonly DependencyProperty SelectedLocationProperty =
DependencyProperty.Register("SelectedLocation",
typeof(SelectedLocation),
typeof(StartupView),
new FrameworkPropertyMetadata(null));
}
XAML of StartupView:
<UserControl x:Class="Test.StartupView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:self="clr-namespace:HandyCopy"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Name="Root">
<WrapPanel>
<ComboBox ItemsSource="{Binding Path=Layouts,ElementName=Root}"
SelectedItem="{Binding Path=SelectedLocation.Layout, ElementName=Root}"/>
</WrapPanel>
</UserControl>
Tried may approches to displaying a "no data" if there are no items in listbox. Since I'm on wp7 and using silverlight I can't use DataTriggers, so I've created a control to have it behave consistently across the whole app. BUT I if you set the breakpoint for the set method - it's not being called at all!
The control class
public class EmptyListBox : ListBox
{
public new IEnumerable ItemsSource
{
get
{
return base.ItemsSource;
}
set
{
// never here
base.ItemsSource = value;
ItemsSourceChanged();
}
}
protected virtual void ItemsSourceChanged()
{
bool noItems = Items.Count == 0;
if (noItems)
{
if (Parent is System.Windows.Controls.Panel)
{
var p = Parent as Panel;
TextBlock noData = new TextBlock();
noData.Text = "No data";
noData.HorizontalAlignment = HorizontalAlignment;
noData.Width = Width;
noData.Height = Height;
noData.Margin = Margin;
p.Children.Add(noData);
Visibility = System.Windows.Visibility.Collapsed;
}
}
}
}
This is xaml
<my:EmptyListBox ItemsSource="{Binding Path=MyData}" Name="myListBox">
<my:EmptyListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=name}" />
</DataTemplate>
</my:EmptyListBox.ItemTemplate>
</my:EmptyListBox>
Codebehind:
ClientModel ClientInfo { get; set; }
public ClientView()
{
ClientInfo = new ClientModel();
ClientInfo.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(DataReady);
DataContext = ClientInfo
}
ClientModel class:
public class ClientModel : INotifyPropertyChanged
{
MyData _myData;
public MyData MyData
{
get
{
return _myData;
}
set
{
_myData = value;
NotifyPropertyChanged("MyData");
}
}
public void GetClient(int id)
{
// fetch the network for data
}
}
LINK TO SOLUTION .ZIP THAT SHOWS THE PROBLEM
http://rapidshare.com/files/455900509/WindowsPhoneDataBoundApplication1.zip
Your new ItemSource should be a DependencyProperty.
Anything that is working with Bindings have to be a DependencyProperty.
Simply make it a DependencyProperty.
I think the solution I'd go for is something like this:
Define a new visual state group ItemsStates and two visual states: NoItems and HasItems.
In the ControlTemplate for your custom listbox, add the visual tree for your "no data" state.
In the NoItems state, set the Visibility of your "no data" elements to Visible and set the Visibility of the default ItemsPresenter to Collapsed.
In the HasItems state, swap the Visibility of these elements.
In an OnApplyTemplate override switch to the Empty state by default: VisualStateManager.GoToState(this, "Empty", true);
In an OnItemsChanged override, check whether the items source is empty and use VisualStateManager to switch between these states accordingly.
That should work :)
Create ItemsSource as a DependencyProperty.
Example:
public IEnumerable ItemsSource
{
get { return (IEnumerable)base.GetValue(ItemsSourceProperty); }
set { base.SetValue(ItemsSourceProperty, value); }
}
public static DependencyProperty ItemsSourceProperty =
DependencyProperty.Register(
"ItemsSource",
typeof(IEnumerable),
typeof(EmptyListBox),
new PropertyMetadata(null));
try to implement the INotifyPropertyChanged interface and use for ItemsSource an ObservableCollection. In the Setter of your Property just call the OnPropertyChanged method.
Maybe this will help.
Try adding Mode=TwoWay to the ItemsSource binding:
<my:EmptyListBox ItemsSource="{Binding Path=MyData, Mode=TwoWay}" Name="myListBox">
<my:EmptyListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=name}" />
</DataTemplate>
</my:EmptyListBox.ItemTemplate>
</my:EmptyListBox>
I am trying to reproduce the BingMaps sample of the Windows Phone 7 trainingkit:
http://msdn.microsoft.com/en-us/wp7trainingcourse_usingbingmapslab_topic2.aspx#_Toc271039352
but instead of wiring everything in codebehind i'd like to use a viewmodel.
Everything works fine except binding to the Mode property (aerial or road) causes a XamlParseException.
Is there a problem because it isn't a simple property?
This is the original Xaml:
<my:Map Name="Map"
CredentialsProvider="{Binding CredentialsProvider}">
<my:Map.Mode>
<my:AerialMode ShouldDisplayLabels="True" />
</my:Map.Mode>
</my:Map>
The Map.Mode can be changed from codebehind.
Instead I am trying the following:
<my:Map x:Name="Map"
CredentialsProvider="{Binding CredentialsProvider}"
ZoomLevel="{Binding Zoom, Mode=TwoWay}"
Center="{Binding Center, Mode=TwoWay}"
Mode="{Binding MapMode}" />
and the important part of the viewmodel:
private MapMode _mapMode = new AerialMode(true);
public MapMode MapMode
{
get { return _mapMode; }
set
{
_mapMode = value;
RaisePropertyChanged("MapMode");
}
}
private void ChangeMapMode()
{
if (MapMode is AerialMode)
{
MapMode = new RoadMode();
}
else
{
MapMode = new AerialMode(true);
}
}
Thanks for your help!
Solved.
"Mode" isn't a dependency property. So it cannot be bound.
My workaround:
added dependency property to view (=Page)
bound dependency property to property in viewmodel (via code in the constructor)
Set Mode of Map control in the propertyChanged callback handler
//Constructor
public MainPage()
{
InitializeComponent();
DataContext = new MainViewModel();
Binding b = new Binding("MapMode");
this.SetBinding(MapModeProperty, b);
}
//DependencyProperty. No need for corresponding CLR-property.
public static readonly DependencyProperty MapModeProperty =
DependencyProperty.Register("MapMode", typeof(MapMode), typeof(MainPage),
new PropertyMetadata(OnMapModeChanged));
//Callback
private static void OnMapModeChanged(DependencyObject element,
DependencyPropertyChangedEventArgs e)
{
((MainPage)element).Map.Mode = e.NewValue as MapMode;
}
Hope this one will help others!
I suspect you'll need to use a converter with your binding.