How can I bind this View to this ViewModel? - c#

The following code-behind binding works for the SmartFormView user control:
View:
<UserControl x:Class="CodeGenerator.Views.PageItemManageSettingsView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:v="clr-namespace:CodeGenerator.Views"
xmlns:vm="clr-namespace:CodeGenerator.ViewModels"
Background="#ddd">
<Grid Margin="10">
<ScrollViewer DockPanel.Dock="Top">
<StackPanel Margin="10">
<v:SmartFormView/>
</StackPanel>
</ScrollViewer>
</Grid>
</UserControl>
Code-behind:
using System.Windows.Controls;
using CodeGenerator.ViewModels;
namespace CodeGenerator.Views
{
public partial class SmartFormView : UserControl
{
public SmartFormView()
{
InitializeComponent();
DataContext = new SmartFormViewModel("testing");
}
}
}
However, I want to bind the SmartFormView to its SmartFormViewModel in the ViewModel of the calling View, not hard-coded in the code-behind. Yet these two approaches don't bind:
<UserControl.Resources>
<DataTemplate DataType="{x:Type vm:SmartFormViewModel}">
<v:SmartFormView/>
</DataTemplate>
</UserControl.Resources>
...
<Grid Margin="10">
<ScrollViewer DockPanel.Dock="Top">
<StackPanel Margin="10">
<TextBlock Text="{Binding Testing}"/>
<v:SmartFormView DataContext="{Binding SmartFormViewModel}"/>
<ContentControl Content="{Binding SmartFormViewModel}"/>
</StackPanel>
</ScrollViewer>
</Grid>
In the ViewModel I have "Testing" and "SmartFormViewModel" defined as ViewModel properties and fill them both (as shown below), but although the Testing property binds fine, the the SmartFormView does not bind to its SmartFormViewModel:
private SmartFormViewModel _smartFormViewModel=;
public SmartFormViewModel SmartFormViewModel
{
get
{
return _smartFormViewModel;
}
set
{
_smartFormViewModel = value;
OnPropertyChanged("SmartFormViewModel");
}
}
private string _testing;
public string Testing
{
get
{
return _testing;
}
set
{
_testing = value;
OnPropertyChanged("Testing");
}
}
public PageItemManageSettingsViewModel(MainViewModel mainViewModel, PageItem pageItem)
: base(mainViewModel, pageItem)
{
SmartFormViewModel SmartFormViewModel = new SmartFormViewModel("manageSettingsMain");
Testing = "test ok";
}
What is the syntax to bind a UserControl in XAML to a specific ViewModel in the calling View's ViewModel?

Could be wrong, but I think you just have a bug in your code.
SmartFormViewModel SmartFormViewModel = new SmartFormViewModel("manageSettingsMain");
Should be:
SmartFormViewModel = new SmartFormViewModel("manageSettingsMain");
ie. Your SmartFormViewModel is never being set. Therefore, the binding you have in your parent view doesn't find it.
Further to this, a better way to do this is just to stick your child VM into the visual tree:
<ContentControl Content="{Binding SmartFormViewModel}"/>
And use a DataTemplate to do the resolution of the view rather than "hard-coding" the view into the, um, parent view.

Related

WPF Window hide Content DependencyProperty

I want to create a Window which redeclares it's own DependencyProperty named Content.
public partial class InfoWindow : Window
{
public static new readonly DependencyProperty ContentProperty = DependencyProperty.Register("Content", typeof(object), typeof(InfoWindow), new PropertyMetadata(null));
public object Content
{
get { return GetValue(ContentProperty); }
set { SetValue(ContentProperty, value); }
}
}
And XAML bind this property
<ContentControl Content="{Binding ElementName=_this, Path=Content}" />
It works fine, just the Visual Studio Designer complains Logical tree depth exceeded while traversing the tree. This could indicate a cycle in the tree.
Is there any way how to tell the Designer that binding is to the InfoWindow.Content and not Window.Content? Or is it a bad idea hide the property and should I renamed my property?
What I am trying to achieve here is the idea of dynamically defining the Buttons that are used to bring up different views for navigating to different forms. (See below: )
The link between the View and View Models are setup inside the Dictionary View_ViewModel which is used to identify the view to set for the Current view when the button is pressed.
(Note: I have tried to use the most basic objects avoiding IOC containers and such like, so as to make it easier to understand the code)
The most important thing to remember is to set the DataContext correctly otherwise you will get the Logical tree depth exceeded while traversing the tree. Error. You could either do this in the Code behind of the View or inside the XAML.
Example:
public partial class SetupForm : UserControl
{
public SetupForm()
{
InitializeComponent();
DataContext = new SetupFormVM();
}
}
OR
<UserControl.DataContext>
<SaleVM:SalesEntryVM />
</UserControl.DataContext>
Here is a code snippet which probably explains it more clearly and probably answers your question.
The view model defines the how many buttons and views you want in the Main Window. This is achieved by having ItemsControl binding to a list in View Model class.
The Button command is bounded to ICommand ChangeViewCommand property in the View Model class which evaluates the Button pressed and .calls ViewChange method which changes the CurrentView (this is bound to the Content in the XAML)
This is what my main Window xaml looks like.
<Window x:Class="MyNameSpace.Views.ApplicationWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MyNameSpace.Views"
mc:Ignorable="d"
Title="ApplicationWindow" Height="Auto" Width="Auto">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition /> -------------------> repeated five times
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition /> -------------------------> repeated five times
</Grid.RowDefinitions>
<DockPanel Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="5" >
<!-- Bind to List of Pages -->
<ItemsControl ItemsSource="{Binding ControlItemsNamesList}" DockPanel.Dock="Top" >
<!-- Stack the buttons horizontally --> The list contains the labels to assign to the buttons
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate> ---------------------------------------> This to stack the buttons Horizontally
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!-- This looks at the list items and creates a button with ControlName -->
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding ControlName}"
Command="{Binding DataContext.ChangeViewCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" ------> This is important for the Buttons to work Window or ContentControl.
CommandParameter="{Binding }"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DockPanel>
<ContentControl Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="5" Grid.RowSpan="4" Content="{Binding CurrentView}"/> ---------> This is where I want the new Windows to appear when I click the button
</Grid>
This is what one of my User control xaml looks like that will appear when I click the button in the Main Window.
<UserControl x:Class="MyNameSpace.Views.SetupForm"
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="450" d:DesignWidth="800">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition /> ------------------- repeated five times
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/> ------------------- repeated five times
</Grid.RowDefinitions>
<DockPanel Grid.Row="0" Grid.Column="0" Grid.RowSpan="5" Background="AliceBlue" Margin="0,0,0,0" >
<!-- Bind to List of Pages -->
<ItemsControl ItemsSource="{Binding ControlItemsNamesList}" DockPanel.Dock="Left" >
<!-- Stack the buttons horizontally -->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!-- This looks at the list items and creates a button with ControlName -->
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding ControlName}"
Command="{Binding DataContext.ChangeViewCommand, RelativeSource={RelativeSource AncestorType={x:Type ContentControl}}}"
CommandParameter="{Binding }"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DockPanel>
<ContentControl Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="5" Grid.RowSpan="4" Content="{Binding CurrentView}"/>
</Grid>
</UserControl>
This is the ViewModel for the Main window:
using Products.MVVMLibrary;
using Products.MVVMLibrary.Interfaces;
using MyNameSpace.Views;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Controls;
using System.Windows.Input;
namespace MyNameSpace.ViewModel
{
public class ApplicationVM : ObservableObject
{
private MyNameSpace IControlItem currentNavigationItem;
private MyNameSpace ContentControl currentView;
private MyNameSpace List<IControlItem> NavigationList;
private MyNameSpace ICommand changeViewCommand;
private MyNameSpace ViewConverter viewDictionary;
private MyNameSpace Dictionary<string, LinkViewToViewModel> View_ViewModel;
public ApplicationVM()
{
viewDictionary = new ViewConverter();
View_ViewModel = new Dictionary<string, LinkViewToViewModel>();
NavigationList = new List<IControlItem>();
InitialiseLists();
}
private MyNameSpace void AddControlNavigationItems(string name, ContentControl view, ObservableObject viewModel)
{
View_ViewModel.Add(name, new LinkViewToViewModel(view, viewModel));
IControlItem item = (IControlItem)viewModel;
NavigationList.Add(item);
}
private MyNameSpace void InitialiseLists()
{
AddControlNavigationItems("Sales", new SalesForm(), new SalesEntryVM());
AddControlNavigationItems("Purchases", new PurchaseEntryForm(), new PurchasesVM());
AddControlNavigationItems("Setup", new SetupForm(), new SetupFormVM());
//Use the property instead which creates the instance and triggers property change
CurrentViewModel = (IControlItem)View_ViewModel[View_ViewModel.Keys.ElementAt(0)].ViewModel;
CurrentView = View_ViewModel[View_ViewModel.Keys.ElementAt(0)].View;
}
public List<IControlItem> ControlItemsNamesList
{
get => NavigationList;
}
/// <summary>
/// Provides a list of names for Navigation controls to the control item
/// </summary>
public Dictionary<string, LinkViewToViewModel> ApplicationViews
{
get
{
return View_ViewModel;
}
set
{
View_ViewModel = value;
}
}
public ContentControl CurrentView
{
get
{
return currentView;
}
set
{
currentView = value;
OnPropertyChanged("CurrentView");
}
}
public IControlItem CurrentViewModel
{
get
{
return currentNavigationItem;
}
set
{
if (currentNavigationItem != value)
{
currentNavigationItem = value;
OnPropertyChanged("CurrentViewModel");
}
}
}
/// <summary>
/// This property is bound to Button Command in XAML.
/// Calls ChangeViewModel which sets the CurrentViewModel
/// </summary>
public ICommand ChangeViewCommand
{
get
{
if (changeViewCommand == null)
{
changeViewCommand = new ButtonClick(
p => ViewChange((IControlItem)p), CanExecute);
}
return changeViewCommand;
}
}
#region Methods
private MyNameSpace void ViewChange(IControlItem viewname)
{
foreach (KeyValuePair<string, LinkViewToViewModel> item in View_ViewModel)
{
if (item.Key == viewname.ControlName)
{//Set the properties of View and ViewModel so they fire PropertyChange event
CurrentViewModel = (IControlItem)item.Value.ViewModel;
CurrentView = item.Value.View;
break;
}
}
}
private MyNameSpace bool CanExecute()
{
return true;
}
#endregion
}
}
Other classes
public class LinkViewToViewModel
{
public LinkViewToViewModel(ContentControl view, ObservableObject viewModel)
{
View = view;
ViewModel = viewModel;
}
public ContentControl View { get; set; }
public ObservableObject ViewModel { get; set; }
}

Assigning datacontext to autogenerated tabitem in tabcontrol

I am trying to assign an object to datacontext from TabItem. To get an idea, look at the following code sample
<UserControl x:Class="CustomCopyNas.UserControls.LoginUsers"
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"
xmlns:igWindows="http://infragistics.com/Windows"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid Margin="5,0,5,0">
<igWindows:XamTabControl Name="_xamTabControl"
TabLayoutStyle="MultiRowSizeToFit"
MaximumTabRows="4"
MaximumSizeToFitAdjustment="50"
MinimumTabExtent="100"
InterTabSpacing="2"
InterRowSpacing="2"
Theme="Metro"
AllowTabClosing="False"
TabItemCloseButtonVisibility="WhenSelectedOrHotTracked">
<igWindows:XamTabControl.ContentTemplate>
<DataTemplate>
<TextBox Text="{Binding Prop}"/>
</DataTemplate>
</igWindows:XamTabControl.ContentTemplate>
</igWindows:XamTabControl>
</Grid>
</UserControl>
As you can see, I use datatemplate for TabItem content appearance, TextBox. The TextBox Text property is binding to a property from the datacontext.
And the partial class from UserControl
public class Foo
{
public string Prop {
get { return "Hello Foo"; }
}
}
/// <summary>
/// Interaction logic for LoginUsers.xaml
/// </summary>
public partial class LoginUsers : UserControl
{
public LoginViewModel LoginViewModel = new LoginViewModel("file.xml");
public LoginUsers()
{
InitializeComponent();
foreach (var server in LoginViewModel.ServerUsers)
{
string header = server.Server;
string name = "tabItem" + header;
_xamTabControl.Items.Add(new TabItemEx() { Header = header, Name = name, DataContext = new Foo() });
}
}
}
As output on TabItem content I've got nothing, so emtpy content, why?
You don't seem to have declared your TabControl XAML properly. It is customary to see it defined more like this, using the TabControl.ItemsSource property:
<TabControl ItemsSource="{Binding YourCollectionProperty}">
<TabControl.ItemTemplate> <!-- Header Template-->
<DataTemplate>
<TextBlock Text="{Binding HeaderText}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate> <!-- Body Template-->
<DataTemplate>
<TextBlock Text="{Binding BodyText}" />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
For this to work, you'll need to create a custom class that has HeaderText and BodyText properties in it. Then you'll need to create a public ObservableCollection<YourCustomClass> collection property in your code behind named YourCollectionProperty.
Please note that the Bindings inside the two DataTemplates will automatically have their DataContexts set to an item from the YourCollectionProperty collection and that is why your Binding to the Prop property didn't work.
Try moving the foreach loop so that it fires when the _xamTabControl.Loaded event fires. That should do the trick

My WPF custom control's Data Context is superseding parent's

In my main window, I try to bind to a bool, but it's looking in my custom control's DataContext instead. If I don't assign DataContext in the user control, then the main window's bindings works, but (obviously) this brakes the bindings in the user control.
Here's the error:
System.Windows.Data Error: 40 : BindingExpression path error: 'MyControlVisible' property not found on 'object' ''MyUserControlModel' (HashCode=1453241)'. BindingExpression:Path=MyControlVisible; DataItem='MyUserControlModel' (HashCode=1453241); target element is 'MyUserControl' (Name='_myUserControl'); target property is 'Visibility' (type 'Visibility')
I need binding to work on both controls, but I don't want the user control's DataContext to supersede the window's.
Here's the code:
<Window x:Class="Sandbox.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Controls="clr-namespace:Sandbox.Controls" Title="Sandbox">
<DockPanel LastChildFill="True">
<DockPanel.Resources>
<BooleanToVisibilityConverter x:Key="boolToVis" />
</DockPanel.Resources>
<Grid>
<Controls:MyUserControl x:Name="_myUserControl" Visibility="{Binding MyControlVisible, Converter={StaticResource boolToVis}}"/>
</Grid>
</DockPanel>
</Window>
namespace Sandbox
{
public partial class MainWindow
{
private MainWindowModel model;
public MainWindow()
{
InitializeComponent();
DataContext = model = new MainWindowModel();
_myUserControl.Initialize(model.MyUControlModel);
}
}
}
using System.ComponentModel;
using Sandbox.Controls;
namespace Sandbox
{
public class MainWindowModel : BaseModel
{
public MyUserControlModel MyUControlModel { get; set; }
public bool MyControlVisible { get; set; }
public MainWindowModel()
{
MyUControlModel = new MyUserControlModel();
MyControlVisible = false;
OnChange("");
}
}
public class BaseModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnChange(string s)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(s));
}
}
}
}
<UserControl x:Class="Sandbox.Controls.MyUserControl"
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">
<Grid>
<TextBlock Text="{Binding MyBoundText}"/>
</Grid>
</UserControl>
namespace Sandbox.Controls
{
public partial class MyUserControl
{
public MyUserControl()
{
InitializeComponent();
}
public void Initialize(MyUserControlModel context)
{
DataContext = context;
}
}
}
namespace Sandbox.Controls
{
public class MyUserControlModel : BaseModel
{
public string MyBoundText { get; set; }
public MyUserControlModel()
{
MyBoundText = "Hello World!";
OnChange("");
}
}
}
That is one of the many reasons you should never set the DataContext directly from the UserControl itself.
When you do so, you can no longer use any other DataContext with it because the UserControl's DataContext is hardcoded in.
In the case of your binding, normally the DataContext would be inherited so the Visibility binding could find the property MyControlVisible on the current DataContext, however because you hardcoded the DataContext in your UserControl's constructor, that property is not found.
You could specify a different binding source in your binding, such as
<Controls:MyUserControl Visibility="{Binding
RelativeSource={RelativeSource AncestorType={x:Type Window}},
Path=DataContext.MyControlVisible,
Converter={StaticResource boolToVis}}" ... />
However that's just a workaround for the problem for this specific case, and in my view is not a permanent solution. A better solution is to simply not hardcode the DataContext in your UserControl
There are a few different ways you can do depending on your UserControl's purpose and how your application is designed.
You could create a DependencyProperty on your UserControl to pass in the value, and bind to that.
<Controls:MyUserControl UcModel="{Binding MyUControlModelProperty}" ... />
and
<UserControl x:Class="Sandbox.Controls.MyUserControl"
ElementName=MyUserControl...>
<Grid DataContext="{Binding UCModel, ElementName=MyUserControl}">
<TextBlock Text="{Binding MyBoundText}"/>
</Grid>
</UserControl>
Or you could build your UserControl with the expectation that a specific property will get passed to it in the DataContext. This is normally what I do, in combination with DataTemplates.
<Controls:MyUserControl DataContext="{Binding MyUControlModelProperty}" ... />
and
<UserControl x:Class="Sandbox.Controls.MyUserControl"...>
<Grid>
<TextBlock Text="{Binding MyBoundText}"/>
</Grid>
</UserControl>
As I said above, I like to use DataTemplates to display my UserControls that expect a specific type of Model for their DataContext, so typically my XAML for the main window would look something like this:
<DataTemplate DataType="{x:Type local:MyUControlModel}">
<Controls:MyUserControl />
</DataTemplate>
<ContentPresenter Content="{Binding MyUControlModelProperty}" ... />

Can I bind a WPF control to a field's property?

Because I needed to split some functionality between classes, I've arrived at the following situation
xaml code
<CheckBox IsChecked="{Binding MyObjectField.MyBoolean}" />
view model
...
public MyInternalObject MyObjectField;
...
MyObject class
public class MyInternalObject {
...
public bool MyBoolean { get; set; }
...
}
It does not work unless I replicate the MyBoolean property in the View Model class.
public bool MyBoolean
{
get { return MyInternalObject.MyBoolean; }
set { MyInternalObject.MyBoolean=value; }
}
Does anyone have an idea?
You can't yet (in WPF Version 4.5 you can bind to a static property). But you can create your property in App.xaml.cs
public partial class App : Application
{
public bool MyBoolean { get; set; }
}
and bind from everywhere.
<CheckBox IsChecked="{Binding MyBoolean, Source={x:Static Application.Current}}">
No you cant . Because binding system uses Reflection to find the
Property in DataContext(i.e your VM)
It does not look for fields . I hope this will help.
Instead of binding an element to a field's property I changed the DataContext of the element to the required field.
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
MainWindowView mainWindowView = new MainWindowView();
var mainWindowViewModel = new MainWindowViewModel();
mainWindowView.DataContext = mainWindowViewModel;
mainWindowView.pagerView.DataContext = mainWindowViewModel.pager;
mainWindowView.Show();
}
In this example I have a DataGrid and Pager (first, prev, next, last page) below it. The elements of the MainWindowView (including the DataGrid) are binded to properties in the MainWindowViewModel but the pager buttons are binded to the properties of mainWindowViewModel.pager.
MainWindowView:
<DataGrid Name="dgSimple" ItemsSource="{Binding DisplayedUsers}" MaxWidth="200" Grid.Row="0" SelectedItem="{Binding SelectedRow}"></DataGrid>
<view:PagerView x:Name="pagerView" Grid.Row="2"/>
PagerView:
<UserControl x:Class="wpf_scroll.View.PagerView"
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"
xmlns:local="clr-namespace:wpf_scroll.View"
mc:Ignorable="d"
d:DesignHeight="30" d:DesignWidth="350">
<StackPanel Orientation="Horizontal" Grid.Row="1">
<Label Content="Page size:"/>
<TextBox Text="{Binding PageSize}" Width="30" VerticalContentAlignment="Center"
HorizontalContentAlignment="Center"></TextBox>
<Button Content="First" Command="{Binding FirstPageCommand}"></Button>

How to bind a Combobox ItemSource to a Property not in the DataContext?

I have a Dialog box, ConfigSetup that has a Combobox. Its data context is set to the viewModel, but I need to bind the ItemSource of my Combobox to a property in the main window( MainWindow).
public partial class MainWindow : Window, INotifyPropertyChanged
{
...
public CfgData.TMicMode[] MicModeOptions
{
get
{
return (CfgData.TMicMode[])System.Enum.GetValues(typeof(CfgData.TMicMode));
}
}
}
Here's where the viewModel is setup in the dialog box code
public partial class ConfigSetup : Window, INotifyPropertyChanged
{
private ConfigSetupVM vm_ = null;
public ConfigSetup(CfgData cfgData)
{
vm_ = new ConfigSetupVM(cfgData);
InitializeComponent();
vm_.RequestClose += delegate
{
Close();
};
DataContext = vm_;
}
}
Here's the code in the VM that has the selectedvalue property to bind to
class ConfigSetupVM : ViewModelBase, IDataErrorInfo
{
...
/// <summary>
/// C-5000's microphone mode.
/// </summary>/
public CfgData.TMicMode MicMode
{
get { return model_.MicMode; }
set { model_.MicMode = value; NotifyPropertyChanged("MicMode"); }
}
Here's the XAML with the combobox
<Window x:Class="RpP25.ConfigSetup"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:RpWin="clr-namespace:RpP25"
Title="FCT Configuration"
Width="300"
SizeToContent="Height"
ResizeMode="NoResize"
WindowStartupLocation="CenterOwner" WindowStyle="ToolWindow"
FocusManager.FocusedElement="{Binding ElementName=name}"
Background="AliceBlue" >
<Window.Resources>
...
</Window.Resources>
...
<ComboBox Grid.Row="6" Grid.Column="1"
HorizontalAlignment="Right" MinWidth="75"
ItemsSource="{Binding RpWin:MainWindow.MicModeOptions, Mode=OneWay}"
SelectedValue="{Binding RpWin:MainWindow.MicMode, Mode=TwoWay, TargetNullValue=Not Selected,
ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged, NotifyOnValidationError=True}" />
...
I know I'm missing something fundamental to Binding, but I can't for the life of figure out how to bind to something outside the datacontext.
I've tried to use FindAncestor... with no success
You help would be greatly appreciated.
There are two possible ways. The one is, as the code below, to use the static member.
<ComboBox ItemsSource="{Binding Source={x:Static local:MainWindow.MicModeOptions} , Mode=OneWay}"/>
public partial class MainWindow : Window, INotifyPropertyChanged
{
public **static** CfgData.TMicMode[] MicModeOptions
{
}
}
The other is to use Resources in XAML, where the target class(MainWindow in your code) has to get a default constructor(parameterless).
<Grid>
<Grid.Resources>
<local:MainWindow x:Key="mainWindow"/>
</Grid.Resources>
<ComboBox ItemsSource="{Binding Source={StaticResource mainWindow}, Path=MicModeOptions , Mode=OneWay}"/>
</Grid>
How is the dialog window launched? If it is launched via window.ShowDialog() then you could pass the necessary object you need to bind to as a parameter to the constructor of your dialog window. The constructor then assigns it to an internal property to which your XAML code can bind to.
Try this method, easy and clean.
<!-- In user countrol resources -->
<UserControl.Resources>
<CollectionViewSource Source="{Binding Currencies}" x:Key="Currencies"/>
</UserControl.Resources>
<!-- below inside ex. DataGrid -->
<ComboBox ItemsSource="{Binding Source={StaticResource Currencies}}" IsSynchronizedWithCurrentItem="False"
DisplayMemberPath="IsoCode"
SelectedItem="{Binding BaseCurrency}"/>
<!-- IsSynchronizedWithCurrentItem="False" is important, otherwise ComboBoxes will select same item for each child viewmodel -->
reference to blogpost http://kostylizm.blogspot.ru/2014/04/wpf-combobox-itemssource-bind-to-parent.html

Categories

Resources