I am beginner to WPF and I am facing problem while trying to bind a Dependency Property as the source of a CollectionViewSource.
The user control exposes a DependencyProperty of type List. It is used to present the data in a DataGrid with the help of CollectionViewSource (using it for Filtering, Grouping and Sorting operations).
My MainWindow XAML:
<Window>
<local:CustomUserControl x:Name="CustomUCDataGrid" ListToDisplay="{Binding listFromDB}"/>
<Window>
My MainWindow.cs:
public partial class MainWindow : Window
{
public List<customType> listFromDB{get;set;}
public MainWindow{
listFromDB = GetListFromDB();
InitializeComponent();
this.DataContext = this;
}
}
CustomUserControl.xaml looks something like:
<UserControl x:Name="ParentNode">
<DataGrid DataContext="{Binding ElementName=ParentNode}">
<StackPanel>
<DataGrid x:Name="DirectDataGrid" ItemSource="{Binding ListToDisplay}"/>
<DataGrid x:Name="DataGridWithCVS" ItemsSource="{Binding cvsList.View}"/>
</StackPanel>
</DataGrid>
</UserControl>
CustomUserControl.xaml.cs looks like:
public partial class CustomUserControl: UserControl
{
public List<customType> ListToDisplay{
get { return (List<customType>)GetValue(ListToDisplayProperty); }
set { SetValue(ListToDisplayProperty, value); }
}
public static readonly DependencyProperty ListToDisplayProperty=
DependencyProperty.Register("ListToDisplay", typeof(List<customType>),
typeof(CustomUserControl));
public CollectionViewSource cvsList { get; set; }
public CustomUserControl{
InitializeComponent();
cvsList = new CollectionViewSource();
cvsList.Source = ListToDisplay;
DataGridWithCVS.ItemsSource = CollectionViewSource.GetDefaultView(cvsList);
}
}
Here the DataGrid with name "DirectDataGrid" has no problem in displaying the data supplied to it from the MainWindow, but the DataGrid with name "DataGridWithCVS" doesn't display any data. Couldn't find any errors while debugging.
Things I have already Tried:
Define the CollectionViewSource as a StaticResource inside the CustomUserControl XAML - Can't implement this because, the UserControl doesn't use the DataContext set from the MainWindow. It uses its own DataContext without overriding the MainWindow's DataContext. (<UserControl x:Name="ParentNode"> <DataGrid DataContext="{Binding ElementName=ParentNode}">...).
It's just some kind of madness :)
Leave Code Behind alone.
In your case, apart from declaring DependecyProperty, there should be nothing there.
<UserControl x:Name="ParentNode">
<UserControl.Resources>
<CollectionViewSource x:Key="cvsList"
Source="{Binding ListToDisplay, ElementName=ParentNode}"/>
</UserControl.Resources>
<StackPanel>
<DataGrid x:Name="DirectDataGrid" ItemsSource="{Binding ListToDisplay, ElementName=ParentNode}"/>
<DataGrid x:Name="DataGridWithCVS" ItemsSource="{Binding Mode=OneWay, Source={StaticResource cvsList}}"/>
</StackPanel>
</UserControl>
Related
I'm a beginner on WPF and trying to bind the Items of a ComboBox to an ObservableCollection
I used this code:
XAML
<Window x:Class="comboBinding2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ComboBox x:Name="cmbTest" ItemsSource="{Binding Path=cmbContent}" Width="200" VerticalAlignment="Center" HorizontalAlignment="Center" />
</Grid>
</Window>
C#
public MainWindow()
{
cmbTest.ItemsSource = cmbContent;
cmbContent.Add("test 1");
cmbContent.Add("test 2");
InitializeComponent();
}
public ObservableCollection<string> cmbContent { get; set; }
I don't get any errors on this Code until I try to debug, it throws the error:
TargetInvocationError
An unhandled exception of type 'System.Reflection.TargetInvocationException' occurred in PresentationFramework.dll
Can anybody tell me what I'm doing wrong?
There are a few things wrong with your current implementation. As others have stated, your list is currently NULL, and the DataContext of the Window is not set.
Though, I would recommend (especially since you just started using WPF) is learning to do the binding the more 'correct' way, using MVVM.
See the simplified example below:
First, you want to set the DataContext of your Window. This will allow the XAML to 'see' the properties within your ViewModel.
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}
}
Next, simply set up a ViewModel class that will contain all of the Window's binding elements, such as:
public class ViewModel
{
public ObservableCollection<string> CmbContent { get; private set; }
public ViewModel()
{
CmbContent = new ObservableCollection<string>
{
"test 1",
"test 2"
};
}
}
Lastly, update your XAML so that the binding path matches the collection:
<Grid>
<ComboBox Width="200"
VerticalAlignment="Center"
HorizontalAlignment="Center"
x:Name="cmbTest"
ItemsSource="{Binding CmbContent}" />
</Grid>
public MainWindow()
{
InitializeComponent();
cmbContent=new ObservableCollection<string>();
cmbContent.Add("test 1");
cmbContent.Add("test 2");
cmbTest.ItemsSource = cmbContent;
}
public ObservableCollection<string> cmbContent { get; set; }
The code above don't use any binding, that's mean using it there no need to bind the Combobox's ItemSource, if you wan't to use binding you need to
First: Set the DataContext from the CodeBehind (ViewModel) using :
this.DataContext=this;
or from the Xaml:
DataContext="{Binding RelativeSource={RelativeSource Self}}">
Second : use the Binding in the ItemSource Just like you did ItemsSource="{Binding Path=cmbContent}" you may also considere using INotifyPropertyChanged Interface if you want to Notify the UI in case of any changes in a property
cmbContent is null because you never set it to anything. I'm guessing the error is actually a NullReferenceException, but it is showing up as TargetInvocationException because it is in the constructor of a view.
Also, you're setting the ItemsSource of the ComboBox twice (once in the binding, once in the constructor). You don't need to do that. Pick one. Your binding won't work the way it is written (because the DataContext isn't set) so you should either go with doing it in code, or set up the DataContext (as suggested by Nadia).
I need ListBox with my UserControl listed in it. My UserControl has TextBox. So I want to display property of List's subitem in UserControl's textBox. I have tried a lot of options with DataContext and ElementName - it just doesn`t work. I just stucked on it. The only way to make it work is to remove DataContext binding of UserControl to itself and change Item Property name so it matches to DependencyProperty name - but I need to reuse my control in different viewmodels with different entities so it is almost not possible to use the approach.
Interesting thing is that if I change my UserControl to Textbox and bind Text property of it - everything works. What the difference between Textbox and my UserControl?
So let me just show my code.
I have simplified the code to show only essential:
Control XAML:
<UserControl x:Class="TestControl.MyControl"
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="100" d:DesignWidth="200"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<TextBlock Text="{Binding Text}"/>
</Grid>
</UserControl>
Control CS:
public partial class MyControl : UserControl
{
public MyControl()
{
InitializeComponent();
}
public string Text
{
get {
return (string)this.GetValue(TextProperty); }
set {
this.SetValue(TextProperty, value); }
}
public static DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(MyControl), new propertyMetadata(""));
}
Window XAML:
<Window x:Class="TestControl.MainWindow"
Name="_windows"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestControl"
Title="MainWindow" Height="350" Width="525" >
<Grid Name="RootGrid">
<ListBox ItemsSource="{Binding ElementName=_windows, Path=MyList}">
<ItemsControl.ItemTemplate >
<DataTemplate >
<local:MyControl Text="{Binding Path=Name}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ListBox>
</Grid>
</Window>
Window CS:
public partial class MainWindow : Window
{
public MainWindow()
{
_list = new ObservableCollection<Item>();
_list.Add(new Item("Sam"));
_list.Add(new Item("App"));
_list.Add(new Item("H**"));
InitializeComponent();
}
private ObservableCollection<Item> _list;
public ObservableCollection<Item> MyList
{
get { return _list;}
set {}
}
}
public class Item
{
public Item(string name)
{
_name = name;
}
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
}
This is a pretty big gotcha in XAML. The problem is that when you do this in the user control:
DataContext="{Binding RelativeSource={RelativeSource Self}}"
You change its data context, so that in this line:
<local:MyControl Text="{Binding Path=Name}"/>
The runtime will now attempt to resolve "Name" on the instance of "MyControl", instead of on the inherited data context (ie, the view model). (Confirm this by checking the Output window -- you should see a binding error to that effect.)
You can get around this by, instead of setting the user control's data context that way, using a RelativeSource binding:
<UserControl x:Class="TestControl.MyControl"
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="100" d:DesignWidth="200"
<Grid>
<TextBlock
Text="{Binding Text,RelativeSource={RelativeSource AncestorType=UserControl}}"
/>
</Grid>
</UserControl>
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>
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
A UserControl has 3 Dependency Properties: FormatAvailabilities, Orientation and FullText. FormatAvailabilities is bound to the ItemsSource property of an ItemsControl. Orientation is bound to the Orientation property if the StackPanel which is in the ItemsPanelTemplate within the ItemsControl. FullText is bound to the Visibility property of two TextBlocks inside the DataTemplate of the ItemsControl. I am using two converters to determine which TextBlock to show: a BoolToVisibilityConverter and a BoolToInvertedVisibilityConverter (the latter is an inversion of the former). I copied the Visibility property as-is from the TextBlock (both of them, independently) to the ItemsControl and it works correctly..
It seems that the bindings on the TextBlocks are not working properly because both are always visible. Since they are both binding on the same property but one is inverted, there should never be a possibility for both to be visible at the same time.
I put a breakpoint in my converter and it is never hit, so my guess is that there is an issue with binding from within a repeating control to the outer control in which it is housed.
App.xaml:
<common:BaseApp x:Class="xyz.App" xmlns:converters="clr-namespace:xyz.Converters;assembly=xyz">
<common:BaseApp.RootVisual>
<phone:PhoneApplicationFrame x:Name="RootFrame" Source="/Home.xaml"/>
</common:BaseApp.RootVisual>
<common:BaseApp.Resources>
<converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
<converters:BoolToVisibilityConverter x:Key="BoolToInvertedVisibilityConverter" IfTrue="Collapsed" IfFalse="Visible"/>
</common:BaseApp.Resources>
</common:BaseApp>
UserControl XAML:
<UserControl
x:Name="FormatsControl"
x:Class="xyz.Formats"
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"
mc:Ignorable="d"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
d:DesignHeight="480" d:DesignWidth="480">
<ItemsControl Background="Transparent" ItemsSource="{Binding ElementName=FormatsControl, Path=FormatAvailabilities}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="{Binding ElementName=FormatsControl, Path=Orientation}"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding BindsDirectlyToSource=True}" Margin="0,0,10,0" Visibility="{Binding ElementName=FormatsControl, Path=FullText, Converter={StaticResource BoolToVisibilityConverter}}"/>
<TextBlock Text="{Binding Description}" Margin="0,0,10,0" Visibility="{Binding ElementName=FormatsControl, Path=FullText, Converter={StaticResource BoolToInvertedVisibilityConverter}}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</UserControl>
UserControl CS:
namespace xyz
{
public partial class Formats : UserControl
{
public static readonly DependencyProperty FormatAvailabilitiesDependencyProperty = DependencyProperty.Register("FormatAvailabilities", typeof(FormatAvailability[]), typeof(Formats), null);
public static readonly DependencyProperty OrientationDependencyProperty = DependencyProperty.Register("Orientation", typeof(Orientation), typeof(Formats), new PropertyMetadata(Orientation.Horizontal));
public static readonly DependencyProperty FullTextDependencyProperty = DependencyProperty.Register("FullText", typeof(bool), typeof(Formats), null);
public FormatAvailability[] FormatAvailabilities
{
get { return (FormatAvailability[])base.GetValue(Formats.FormatAvailabilitiesDependencyProperty); }
set { base.SetValue(Formats.FormatAvailabilitiesDependencyProperty, value); }
}
public Orientation Orientation
{
get { return (Orientation)base.GetValue(Formats.OrientationDependencyProperty); }
set { base.SetValue(Formats.OrientationDependencyProperty, value); }
}
public bool FullText
{
get { return (bool)base.GetValue(Formats.FullTextDependencyProperty); }
set { base.SetValue(Formats.FullTextDependencyProperty, value); }
}
public Formats()
{
InitializeComponent();
}
}
}
I must be over looking something...thanks!
There is an issue with naming UserControls in Silverlight 3 as described by this blog post, which is also present in the Windows Phone 7 version of Silverlight. Effectively, if you give the UserControl a name in the XAML where it is used (i.e. it's parent), then that overrides the name given in the UserControl's own XAML file.
I ran into a similar problem, instead of binding to the elementname I changed the binding to this
Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}
And that works just fine.
Looks like you are missing the OnPropertyChanged handler.
Here is one of my dependency properties. Note the changed handler.
public ObservableCollection<ObjWithDesc> ItemsSource
{
get
{
return (ObservableCollection<ObjWithDesc>)GetValue(ItemsSourceProperty);
}
set
{
SetValue(ItemsSourceProperty, value);
}
}
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register(
"ItemsSource",
typeof(ObservableCollection<ObjWithDesc>),
typeof(HorizontalListBox),
new PropertyMetadata(OnItemsSourcePropertyChanged)
);
static void OnItemsSourcePropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
((HorizontalListBox) obj).OnItemsSourcePropertyChanged(e);
}
private void OnItemsSourcePropertyChanged(DependencyPropertyChangedEventArgs e)
{
ObservableCollection<ObjWithDesc> objWithDescList = (ObservableCollection<ObjWithDesc>)e.NewValue;
MainListBox.ItemsSource = objWithDescList;
}