Binding a combobox in XAML to a childwindow property - silverlight

I want to display a child window that contains a combobox with several values coming from one of the child window's property:
public partial class MyChildWindow : ChildWindow
{
private ObservableCollection<MyClass> _collectionToBind = // initialize and add items to collection to make sure it s not empty...
public ObservableCollection<MyClass> CollectionToBind
{
get { return _collectionToBind; }
set { _collectionToBind = value; }
}
}
How do I bind in XAML my combobox to the ComboBoxContent collection (both are in the same class)? I've tried several things such as:
<ComboBox x:Name="linkCombo" ItemsSource="{Binding Path=CollectionToBind }" DisplayMemberPath="Description">
I've only been able to bind it in the code behind file and would like to learn the XAML way to do it.
Thank you!

In this case I would use ElementToElement binding like this:-
<ComboBox x:Name="linkCombo" ItemsSource="{Binding Path=Parent.CollectionToBind, ElementName=LayoutRoot }" DisplayMemberPath="Description">
You give the Content element of the ChildWindow the x:Name of LayoutRoot (in the standard template for child window this is done for you). Hence you can bind to this named element and navigate to the containing ChildWindow by using its Parent property.
Using DataContext = this is tempting and works in simple scenarios but things can get awkward in more complex requirements when the DataContext has already been taken in this way.

You need to set the DataContext of the ChildWindow to what contains the values you'd like to bind to. In this case where you're putting the values you want to bind to on the ChildWindow itself so just put a line in the constructor assigned the DataContext to itself.
DataContext = this;

You can also do this using a RelativeSource binding in the XAML, like this:
{Binding Path=CollectionToBind, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}
However, a better way to do this would be to put the CollectionToBind in a separate class and assign it to the Window DataContext. Now both the Window and the XAML Bindings can all refer to the same class as the DataContext and you can isolate more of your logic into this class rather than putting it in the Window implementation.

Related

Why do I need to set IsSynchronizedWithCurrentItem for certain ComboBox ItemsSource binding sources?

I have a WPF control inheriting from ComboBox whose ItemsSource is bound to a list of elements, and whose SelectedItem is bound to another field. I have two references to the same list in my application: one resides in my MainWindow class, and one resides in my App class.
The list of elements used as the ItemsSource is assigned to the ComboBox at the time it is created. Thus, I expect to get an ItemsSourceChanged event.
The strange thing is, if the ItemsSource is bound to the MainWindow's list, then the ComboBox becomes populated with the correct SelectedItem drawn from the bound field. However, if I bind the ItemsSource to the App's copy of the list, then the SelectedItem becomes overwritten with null due to the ItemsSourceChanged event.
Here is the XAML for the ComboBox when it's binding to the App copy of the list:
<local:TagSelectionComboBox FilteredTagsList="{Binding Path=TagsList, Source={x:Static Application.Current}}" SelectedItem="{Binding TagValue}"></local:TagSelectionComboBox>
Here is the XAML for the ComboBox when it's binding to the MainWindow copy of the list:
<local:TagSelectionComboBox FilteredTagsList="{Binding TagsList, RelativeSource={RelativeSource AncestorType=Window}}" SelectedItem="{Binding TagValue}"></local:TagSelectionComboBox>
Here is the MainWindow's property used for binding:
public ObservableCollection<Tag> TagsList
{
get { return proj.Sim.Tags; }
}
And here is the App's property used for binding:
public ObservableCollection<Tag> TagsList
{
get { return proj.Sim.Tags; }
}
So these two properties on App and MainWindow are returning the same list. Stepping through in the debugger confirms: proj.Sim.Tags contains the same list in both cases.
Why does changing from one binding source to another alter the binding behavior? Is something else going on?
I've found that if I explicitly set IsSynchronizedWithCurrentItem="True", then the behavior is the same in both cases. So it's almost like IsSynchronizedWithCurrentItem="True" is the default behavior when I use the MainWindow (RelativeSource) binding but IsSynchronizedWithCurrentItem="False" is the default behavior when I use the App (x:Static) binding.
Background: the ComboBox-derived control in this question is basically the same as YantingChen's answer for this question:
Dynamic filter of WPF combobox based on text input

Cannot set DataContext of custom UserControl declaratively

I have created a small UserControl named EventMenu. I reference it in a larger view using:
<views:EventMenu
Grid.Row="1"
DataContext="{Binding DataContext.ServicingEventsMenuViewModel}"
/>
(I've also tried DataContext="{Binding Path=DataContext.ServicingEventsMenuViewModel}").
However, no content from the bound data context appears in the control (static content from the control's XAML file does appear). I believe the control is not binding the DataContext.
In the code behind constructor for the UserControl, I've done this:
public EventMenu()
{
InitializeComponent();
if (DataContext == null)
{
Console.WriteLine("No data context!");
}
}
and it does indicate that the DataContext is null.
What do I need to do to ensure that the DataContext is set on my UserControl?
The first wrong thing i see is this :
DataContext="{Binding DataContext.ServicingEventsMenuViewModel}"
DataContext is a Dependency Property of FrameworkElement.
So when writing something like you'v written , you wrote it as if your DataContext had a property called DataContext , it's the equivalent of writing this :
DataContext="{Binding Path=DataContext.ServicingEventsMenuViewModel}"
Are you trying to reach a parent element's DataContext ?
if so you need to write something like this ( for example if it's parent is a UserControl):
DataContext="{Binding Path=DataContext.ServicingEventsMenuViewModel,RelativeSource={RelativeSource AncestorType={x:Type UserControl}"
I'm guessing that's the case here ...
let me ask you this , post the xaml in which
<views:EventMenu />
is nested and the class holding ServicingEventsMenuViewModel property .
The second thing i think is wrong is to checking if your DataContext is null in the constructor , even if you did write the correct binding statement this property would still be null in the constructor.
subscribe to the FrameworkElement's Loaded event and check it there.

trying to bind datagrid item source to a property in another class

I have a WPF app with a MainWindow. The MainWindow consists of several CLR properties of type ObservableCollection. The MainWindow has a datagrid, whose ItemsSource property is bound to one of the observable collections (works fine). Next, I have a dialog. Its purpose is to display one of the observable collections from the main window in a datagrid. The dialog gets instantiated in the MainWindow. Initially I was passing the ObservableCollection to the dialog's constructor, and copying it into the dialog's CLR property. Then I would set the DataContext of the dialog to itself, and bind the ItemsSource property in the datagrid to the name of the CLR property. This worked fine.
Is there a better way to do this instead of passing the observable collection through the constructor? I tried setting the ItemsSource property of the Datagrid in the dialog to the observable collection in the MainWindow by using the GUI editor, which generated a binding using RelativeAncestor, but the data did not show. The problem is I have a bunch of dialogs that are meant to display data from the MainWindow, and I feel like there should be a simpler solution rather than passing everything to dialog's constructor. Also, would the dialogs be considered SubViews? The main window is a view.
Let's say your Dialog control is named DialogControl and has a DependencyProperty named Items defined in its code behind. In the XAML, I would bind this property to the DataGrid like this:
<DataGrid ItemsSource="{Binding Items, RelativeSource={RelativeSource Mode=
FindAncestor, AncestorType={x:Type DialogControl}}" />
This RelativeSource binding will go off and search through the properties of your DialogControl class and find the Items property. Note: Do NOT set the DataContext of the UserControl to itself.
Now in your MainWindow.xaml.cs file where you instantiate your DialogControl, you can set the Items property:
DialogControl dialogControl = new DialogControl();
dialogControl.Items = someCollection;
dialogControl.Show();
UPDATE >>>
Oh I see what you're after now... you want to bind from your UserControl to the actual collection in the MainWindow.xaml.cs file. You can still follow my advice, but instead of having the DependencyProperty in your DialogControl, you need to have it in your MainWindow.xaml.cs file. In that case, your binding in the UserControl would be:
<DataGrid ItemsSource="{Binding Items, RelativeSource={RelativeSource Mode=
FindAncestor, AncestorType={x:Type MainWindow}}" />
For this to work, the Items property must be a DependencyProperty.

WPF MVVM Controls incorrectly sharing ViewModel

I have a control:
DailyHours. That has a collection of custom TimeEdit controls I created. Each control is supposed to bind to the DailyHours view-model class. Instead it tries to find the values I bind to in the TimeEdit control.
An example of a TimeEdit control binding:
<bc:TimeEdit Time="{Binding CurrentOperatingHours.MondayClose}" ></bc:TimeEdit>
The TimeEdit control has its own internal values for hour and minute that are bound to Hour and Minute properties. For this reason the DataContext for the TimeEdit control is itself.
public TimeEdit()
{
InitializeComponent();
this.DataContext = this;
}
However this causes the DailyHours control to incorrectly bind the time value. It looks for the CurrentOperatingHours property in the TimeEdit control instead of its own view-model.
How do I get my TimeEdit control to bind correctly with internal values without disturbing binding on its parent container?
you can go two ways: remove this.Datacontext = this; from your usercontrol and use bindings with ElementName for Hour and Minute (good practise!) or use relativeSourceBinding in your Mainview (bad practise).
i answered a similar question here.
ps: an usercontrol should never set the datacontext to it self. this brokes datacontext inheritance and its not an expected behavior.
try this
Time="{Binding DataContext.CurrentOperatingHours.MondayClose, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}">
I hope this will work .

WPF access info from the containing Control

I have a UserControl(a) with a stackpanel which has its ItemSource set to a collection.
The StackPanel then contains a set of UserControl(b) that contain a few buttons and a datagrid control.
Is there a way from the code behind in the UserControl(b) to access properties in the code behind of the parent UserControl(a).
Basically when UserControl(a) loaded into a window a parameter is passed in that contains whether the form will be considered read only or not. I would like bind the visibility of the buttons in Usercontrol(b) to the readonly property in the codebehind of the parent UserControl(a).
Normally with WPF I'd suggest you implement the Model-View-ViewModel pattern (see MSDN).
With this pattern you'd create a ViewModel with all of the data in that you want to bind. This would be set as the data context for the (a) usercontrol. That control would then bind all of it's controls to properties on the datacontext.
The child (b) usercontrol would inherit this datacontext and could therefore bind it's controls to the same properties as (a) uses. This is because datacontexts are inherited down the logical (and visual) tree until such point as it's overridden.
So for you I'd be looking at creating a ViewModel that contains the property ReadOnly. You can then set this ViewModel object as the datacontext for the (a) usercontrol. The (b) usercontrol, since it's under the (a) usercontrol hierarchy will inherit the same datacontext. This will then allow you to bind controls within (b) to the same properties as (a) as shown below.
<Button IsEnabled="{Binding ReadOnly}"
Context="Click me!"
Command="{Binding ClickMeCommand}" />
To set the datacontext in the view code-behind I do something like this constructor shown below.
public MyView(IMyViewModel viewModel)
{
InitializeComponent();
DataContext = viewModel;
}
MyView is the class that inherits from UserControl in your instance. You don't have to get the viewmodel in the way I have, I'm using Unity to inject the viewmodel into the views that are constructed automatically since I'm using Prism but you can just create it as a normal object and assign it to the datacontext.
Note that I've also bound the command to the button using the datacontext as I usually expose those via the ViewModel too, this is easy if you create a wrapper class that implements ICommand and proxies to a delegate. See DelegateCommand blog article or look at the DelegateCommand class in Prism if you are interested.
If for some reason you do override the datacontext, which can happen when using a master/details view where you change the datacontext of the details section of the view to be the currently selected item in the list, then you can still access the parent datacontext by using a relative source binding.
E.g.
<ComboBox Grid.Row="1" Grid.Column="1" x:Name="Unit" IsReadOnly="True"
ItemsSource="{Binding Path=DataContext.AvailableUnits, RelativeSource=
{RelativeSource Mode=FindAncestor,
AncestorType={x:Type Window}}}"
DisplayMemberPath="Name" SelectedItem="{Binding Unit}" />
Note the ItemsSource binding uses a relative source to find the parent window and then bind to a property of it's datacontext. I've also split the ItemsSource binding within the quotes across multiple lines for clarity here but don't do that in your xaml, I'm not sure it'll work there (not tried to see if markup extensions are that tolerant of whitespace).

Resources