I have a written a control but I am having some issues binding a CollectionViewSource to my ItemsSource property. If I Bind an ObservableCollection to the ItemsSource property everything works as expected. With the CollectionViewSource bound to it I get the following binding error in the output.
Error: Converter failed to convert value of type 'Windows.UI.Xaml.Data.ICollectionView' to type 'IBindableIterable'; BindingExpression: Path='JobView.View' DataItem='App.ViewModel.MainViewModel'; target element is MySpecialControl.MySpecialControl' (Name='null'); target property is 'ItemsSource' (type 'IBindableIterable').
public sealed class MySpecialControl: Control
{
public IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(MySpecialControl), new PropertyMetadata((IEnumerable)null, OnItemSourcePropertyChanged));
private static void OnItemSourcePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((MySpecialControl)sender).OnItemSourcePropertyChanged((IEnumerable) eventAgrs.OldValue, (IEnumerable)eventAgrs.NewValue);
}
private void OnItemSourcePropertyChanged(IEnumerable oldValue, IEnumerable newValue)
{
INotifyCollectionChanged oldCollectionChanged = oldValue as INotifyCollectionChanged;
if (oldCollectionChanged != null)
oldCollectionChanged.CollectionChanged -= ItemSource_CollectionChanged;
INotifyCollectionChanged newCollectionChanged = newValue as INotifyCollectionChanged;
if (newCollectionChanged != null)
newCollectionChanged.CollectionChanged += ItemSource_CollectionChanged;
}
private void ItemSource_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
...
}
}
In my XAML I have bound it to the CollectionViewSource.View
Any ideas how to change the ItemsSource to accept an ObservableCollection as well as a CollectionViewSource.View ?
Thanks
Related
I'm trying to create a WPF ListView that can bind its SelectedItems property.
I inherit from ListView. When the ListView's SelectionChanged event is raised, I read the ListView's SelectedItems property, and set my DependencyProperty's value to it.
I then add a BetterListView object to the XAML and bind its SelectedItemsBindable property to the viewmodel.
And although it looks like everything works - when I break at the view model's set method, I see that although SelectedItemsBindable was set to a collection inside BetterListView, the viewmodel gets this value as null! What am I doing wrong?
class BetterListView : ListView
{
[Bindable(true)]
[Category("Appearance")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public IList SelectedItemsBindable
{
get { return GetValue(SelectedItemsBindableProperty) as IList; }
set
{
SetValue(SelectedItemsBindableProperty, value);
}
}
public static readonly DependencyProperty SelectedItemsBindableProperty = DependencyProperty.Register(
"SelectedItemsBindable",
typeof(IList),
typeof (BetterListView),
new FrameworkPropertyMetadata(new List<object>(),
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
var items = SelectedItems.Cast<object>().ToList();
SelectedItemsBindable = items;
}
#region constructor
public BetterListView()
{
SelectionChanged += OnSelectionChanged;
}
#endregion
}
As the SelectedItems property of the ListBox control is a normal property and not a dependency property to bind to, I have derived of the ListBox and created a new dependency property SelectedItemsEx.
But my XAML compiler keeps giving me the error
A 'Binding' cannot be set on the 'SelectedItemsEx' property of the
type 'MyListBox'. A 'Binding' can only be set on a DependencyProperty
of a DependencyObject.
Why my property is not recognized as a dependency property? Any help is appreciated, thank you!
XAML:
<MyListBox ItemsSource="{Binding MyData}" SelectedItemsEx="{Binding SelectedEx}"
SelectionMode="Extended"> ... </MyListBox>
ListBox' implementation:
public class MyListBox : ListBox
{
public readonly DependencyProperty SelectedItemsExProperty =
DependencyProperty.Register("SelectedItemsEx",
typeof(ObservableCollection<MyItemsDataType>),
typeof(MyListBox),
new PropertyMetadata(default(ObservableCollection<MyItemsDataType>)));
public ObservableCollection<MyItemsDataType> SelectedItemsEx
{
get
{
var v = GetValue(SelectedItemsExProperty);
return (ObservableCollection<MyItemsDataType>)v;
}
set { SetValue(SelectedItemsExProperty, value); }
}
protected override void OnSelectionChanged(SelectionChangedEventArgs e)
{
base.OnSelectionChanged(e);
if (SelectedItemsEx != null)
{
SelectedItemsEx.Clear();
foreach (var item in base.SelectedItems)
{
SelectedItemsEx.Add((MyItemsDataType)item);
}
}
}
The DependencyProperty field must be static:
public static readonly DependencyProperty SelectedItemsExProperty = ...
Note also that in order to make your derived ListBox a little more reusable, you should not constrain the type of the SelectedItemsEx property. Use IEnumerable (or IList like SelectedItems) instead. Moreover, there is no need to specify a default value by property metadata, as it is null already and default(<any reference type>) is also null.
You will however have to get notified whenever the SelectedItemsEx property has changed. Therefore you have to register a change callback via property metadata:
public static readonly DependencyProperty SelectedItemsExProperty =
DependencyProperty.Register(
"SelectedItemsEx", typeof(IEnumerable), typeof(MyListBox),
new PropertyMetadata(SelectedItemsExPropertyChanged));
public IEnumerable SelectedItemsEx
{
get { return (IEnumerable)GetValue(SelectedItemsExProperty); }
set { SetValue(SelectedItemsExProperty, value); }
}
private static void SelectedItemsExPropertyChanged(
DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var listBox = (MyListBox)obj;
var oldColl = e.OldValue as INotifyCollectionChanged;
var newColl = e.NewValue as INotifyCollectionChanged;
if (oldColl != null)
{
oldColl.CollectionChanged -= listBox.SelectedItemsExCollectionChanged;
}
if (newColl != null)
{
newColl.CollectionChanged += listBox.SelectedItemsExCollectionChanged;
}
}
private void SelectedItemsExCollectionChanged(
object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
...
}
}
I can't find an event that can be called after a window was loaded, and where i can get access to the ItemsSource of a ListView. The only thing i can think is the Loaded event in the ListView but when this event is fired the ItemsSource remains null.
I probably need another event so i can know what is in the ItemsSource.
So with code i will probably expose better what i am trying to do:
In a custom class:
public class GridViewSomething
{
public static readonly DependencyProperty TestProperty =
DependencyProperty.RegisterAttached("Test",
typeof(bool),
typeof(GridViewSomething),
new UIPropertyMetadata(OnTestChanged));
public static bool GetTest(DependencyObject obj)
{
return (bool)obj.GetValue(TestProperty);
}
public static void SetTest(DependencyObject obj, bool value)
{
obj.SetValue(TestProperty, value);
}
static void OnTestChanged(object sender, DependencyPropertyChangedEventArgs e)
{
ListView listView = sender as ListView;
if (!(bool)e.OldValue && (bool)e.NewValue)
listView.AddHandler(ListView.LoadedEvent, new RoutedEventHandler(ListView_Loaded));
else if (!(bool)e.NewValue && (bool)e.OldValue)
listView.RemoveHandler(ListView.LoadedEvent, new RoutedEventHandler(ListView_Loaded);
}
static void ListView_Loaded(object sender, RoutedEventArgs e)
{
ListView listView = sender as ListView;
if (listView.ItemsSource != null)
{
//Do some work
}
}
}
And the ListView:
(...)
<ListView ItemsSource="{Binding Students}"
test:GridViewSomething.Test="True">
(...)
I am binding the ListView to a Collection in the ViewModel of this Window. I need to know precisely what is in the ItemsSource in that custom class.
So how i can achieve this?
Thanks in advance!
You could subsribe to changes on the ItemsSource property by using a descriptor as shown here. If check the value in that handler it should not be null (unless the bound property was in fact set to null).
i m inheriting ListBox to my class and i want to override itemsource property.
i actually want to do someoperation when itemsource is assigned.
how it is possible?
i want in c# code only not in xaml
The way you override a dependency property in WPF is this way....
public class MyListBox : ListBox
{
//// Static constructor to override.
static MyListBox()
{
ListBox.ItemsSourceProperty.OverrideMetadata(typeof(MyListBox), new FrameworkPropertyMetadata(null, MyListBoxItemsSourceChanged));
}
private static void MyListBoxItemsSourceChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
var myListBox = sender as MyListBox;
//// You custom code.
}
}
Is this what you are looking for?
Why not just set SourceUpdated event handler before setting the item source property?
For Example if MyListBox is your Listbox & MyItemSource is the source, you may set event handler n call it as follows:
void MyFunction()
{
MyListBox.SourceUpdated += new EventHandler<DataTransferEventArgs>(MyListBox_SourceUpdated);
MyListBox.ItemsSource = MyItemSource;
}
void MyListBox_SourceUpdated(object sender, DataTransferEventArgs e)
{
// Do your work here
}
Also, make sure that your data source implements INotifyPropertyChanged or INotifyCollectionChanged events.
Here i have crated a Custom List box which extends from Listbox and it got a dependency property itemssource...
When ever item source got updated you can do your operations and after that you can call the updatesourcemethod of customlistbox which will assign the itemsSource property of BaseClass.
public class CustomListBox : ListBox
{
public IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
// Using a DependencyProperty as the backing store for ItemsSource. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(CustomListBox), new UIPropertyMetadata(0, ItemsSourceUpdated));
private static void ItemsSourceUpdated(object sender, DependencyPropertyChangedEventArgs e)
{
var customListbox = (sender as CustomListBox);
// Your Code
customListbox.UpdateItemssSource(e.NewValue as IEnumerable);
}
protected void UpdateItemssSource(IEnumerable source)
{
base.ItemsSource = source;
}
}
I am working on WPF project. I create a usercontrol containing a combobox; which represent boolean value(True or false). And I register a DependencyProperty Value for my usercontrol.
Whenever combobox selection was changed, I will update the Value property and also when Value property is update I will update combobox.
But I found the problem when I use my usercontrol in MVVM. I bind the Value property with my IsEnable property in my viewModel. I set binding mode as TwoWay binding. But when I changed selection in comboBox, IsEnable property is never set.
My usercontrol:
public bool Value
{
get { return (bool)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(bool),
typeof(BooleanComboBox),
new UIPropertyMetadata(true, OnValuePropertyChanged));
private void Cmb_Selection_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ComboBox cmb = sender as ComboBox;
object selectedValue = cmb.SelectedValue;
if (selectedValue == null)
{
this.Value = false;
}
else
{
if (selectedValue.GetType() == typeof(bool))
{
this.Value = (bool)selectedValue;
}
else
{
this.Value = false;
}
}
if (this.OnValueChange != null)
this.OnValueChange(this, this.Value);
}
private static void OnValuePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
BooleanComboBox self = sender as BooleanComboBox;
self.Cmb_Selection.SelectedValue = (bool)args.NewValue;
}
In window, where I place my usercontrol (I already set usercontrol's datacontext to my viewModel):
<tibsExtControl:BooleanComboBox Grid.Row="4"
Grid.Column="1"
VerticalAlignment="Center"
Value="{Binding Path=NewTemporaryZone.IsEnable,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"
x:Name="Cmb_AllowNonLBILogon"/>
In my model class I declare an IsEnable property:
private bool _isEnable;
public bool IsEnable
{
get { return _isEnable; }
set
{
_isEnable= value;
OnPropertyChanged("IsEnable");
}
}
What's going on with my usercontrol. I miss something ? Please help me. T.T
Please check whether you have any binding error in the output window of VS.
Try refreshing your binding in Cmb_Selection_SelectionChanged. Something like:
BindingExpression b = cmb.GetBindingExpression(MyNamespace.ValueProperty);
b.UpdateSource();
I've had the same problem; with boolean dependency properties! Try switching the bool to a INullable<bool> (bool?) and apply the appropriate type conversions. This worked for me. Don't know if this is a bug or if value types are handled somewhat different compared to reference types when creating dependency properties? Maybe someone else could verify that.