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).
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
}
I'm working on a custom WPF UserControl and having an issue with one of my DependencyProperties.
So I built a test scenario that looks like this. In the Custom Control..
public static readonly DependencyProperty MyCollectionItemsSourceProperty = DependencyProperty.Register("DynamicHeaderItemsSource", typeof(IEnumerable), typeof(TestUserControl1),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender, new PropertyChangedCallback(OnMyCollectionItemsSourceChanged)));
public IEnumerable MyCollectionItemsSource
{
get { return (IEnumerable)GetValue(MyCollectionItemsSourceProperty ); }
set { SetValue(MyCollectionItemsSourceProperty , value); }
}
protected static void OnMyCollectionItemsSourceChanged(DependencyObject property, DependencyPropertyChangedEventArgs args)
{
System.Diagnostics.Debug.WriteLine("MyCollection Updated");
}
In my test window's code behind:
public ObservableCollection<string> MyTestStrings { get; set; }
private void Window_Loaded(object sender, RoutedEventArgs e)
{
MyTestStrings.Add("First");
MyTestStrings.Add("Second");
MyTestStrings.Add("Third");
}
And in my test window's XAML:
<Grid>
<local:TestUserControl1 MyCollectionItemsSource="{Binding MyTestStrings}">
</Grid>
The problem is, I never get a notification of any type when the underline collection changes. The OnMyCollectionItemsSourceChanged only ever gets called once: at the beginning when the binding is set. What am I missing?
It is an expected behavior your MyCollectionItemsSource just change when it is set in XAML binding since (one time )t hen those adds in the collection is not changing your property itself (it is doing something inside of the collection).
if you want to get information about changing collection you have to first in OnMyCollectionItemsSourceChanged event test if the vale supports INotifyCollectionChanged this then register for NotifyCollectionChangedEventHandler isndie, do not forget to unregister your handler
protected static void OnMyCollectionItemsSourceChanged(DependencyObject property, DependencyPropertyChangedEventArgs args)
{
if( args.OldValue is INotifyCollectionChanged)
(args.OldValue as INotifyCollectionChanged ).CollectionChanged -= CollectionChangedHandler;
if(args.NewValue is INotifyCollectionChanged)
(args.OldValue as INotifyCollectionChanged).CollectionChanged += CollectionChangedHandler;
}
private static void CollectionChangedHandler(object sender, NotifyCollectionChangedEventArgs e)
{
//
}
The PropertyChangedCallback will be called only when the property is set (or nullified) not if there are any changes to the collection itself (adding/removing elements). To do that you will have to hook up to the CollectionChanged event. See this post: https://stackoverflow.com/a/12746855/4173996
The case is: I have a control's event that I want my ViewModel to react on. Currently I'm doing this by executing a command of invisible button like in the example below.
In View.xaml:
<Control x:Name="SearchResultGrid" ... DataRefreshed="SearchResultRefreshed" />
<Button x:Name="SearchResultRefreshedButton" Visibility="Collapsed" Command="{Binding SearchResultRefreshedCommand}" />
In View.xaml.cs:
private void SearchResultRefreshed(object sender, EventArgs e)
{
if (SearchResultRefreshedButton.Command != null)
{
SearchResultRefreshedButton.Command.Execute(SearchResultGrid.ResultRowCount);
}
}
This works good, but it looks like a hack to me. I'm wondering if there is better (standard) way of doing this? I could not find any examples and this is what I "invented" myself.
Using MVVM, the general way to handle events is to simply wrap them in Attached Properties, or use Attached Events. Here is an example using the PreviewKeyDown event in an Attached Property:
public static DependencyProperty PreviewKeyDownProperty = DependencyProperty.RegisterAttached("PreviewKeyDown", typeof(KeyEventHandler), typeof(TextBoxProperties), new UIPropertyMetadata(null, OnPreviewKeyDownChanged));
public static KeyEventHandler GetPreviewKeyDown(DependencyObject dependencyObject)
{
return (KeyEventHandler)dependencyObject.GetValue(PreviewKeyDownProperty);
}
public static void SetPreviewKeyDown(DependencyObject dependencyObject, KeyEventHandler value)
{
dependencyObject.SetValue(PreviewKeyDownProperty, value);
}
public static void OnPreviewKeyDownChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
TextBox textBox = dependencyObject as TextBox;
if (e.OldValue == null && e.NewValue != null) textBox.PreviewKeyDown += TextBox_PreviewKeyDown;
else if (e.OldValue != null && e.NewValue == null) textBox.PreviewKeyDown -= TextBox_PreviewKeyDown;
}
private static void TextBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
TextBox textBox = sender as TextBox;
KeyEventHandler eventHandler = GetPreviewKeyDown(textBox);
if (eventHandler != null) eventHandler(sender, e);
}
Note that it is just as easy (and better too) to use an ICommand instead of the actual KeyEventArgs object which shouldn't really be in the view model. Just create an Attached Property of type ICommand and call that from this TextBox_PreviewKeyDown handler instead:
private static void TextBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
TextBox textBox = sender as TextBox;
ICommand command = PreviewKeyDownCommand(textBox);
if (command != null && command.CanExecute(textBox)) command.Execute(textBox);
}
Either way, it would be used something like this:
<TextBox TextBoxProperties.PreviewKeyDown="SomeKeyEventHandler" />
Or if you used the preferred ICommand method:
<TextBox TextBoxProperties.PreviewKeyDownCommand="{Binding SomeCommand}" />
Personally I've never had a need to use an attached property to deal with a control's event. In your example, of a control wanting to know when the 'SearchResultRefreshed' and then informing the ViewModel through the hidden control ... why doesn't the ViewModel already know that the results have been refreshed?
If the results are coming from the ViewModel in the first place, and binding is used to display them within your control, then the knowledge that the search results have been refreshed should be driven by your ViewModel - not your view.
In only a few cases have I found a need to break away from ICommands and data-binding.
You should add a dependency property DataRefreshed to your control in order to bind on it
here an example how you can do it
public static readonly DependencyProperty DataRefreshedProperty = DependencyProperty.Register(
"DataRefreshed",
typeof(bool),
typeof("typeof yourcontrol here "),
new FrameworkPropertyMetadata(null,
FrameworkPropertyMetadataOptions.AffectsRender,
new PropertyChangedCallback(OnDataRefreshedChanged)
)
);
public bool DataRefreshed
{
get { return (bool)GetValue(DataRefreshedProperty); }
set { SetValue(DataRefreshedProperty, value); }
}
Then you can manipulate your property like any other WPF property for example SearchResultRefreshed which is defined in your ViewModel
<Control x:Name="SearchResultGrid" ... DataRefreshed="{Binding SearchResultRefreshed}" />
<Button x:Name="SearchResultRefreshedButton" Visibility="Collapsed" Command="{Binding SearchResultRefreshedCommand}" />
take a look at the following tutorial to understand more dependecyproperty and attachedproperty
I have a tabcontrol which in the first tab loads an WPF datagrid. The rows in the datagrid are
filled with MemberViewModels.
I have managed to get the selected MemberViewModel when the datagrids selectItem is changed, but how can I pass it on to my RibbonViewModel where I want to add it to the TabItems collection with a command? The RibbonViewModel is the ViewModel of my Ribbon. On that ribbon there is a button which adds a new MemberViewModel to the tabItemsCollection, this works fine. However I want to add the selected MemberViewModel from my datagrid to be added as a new tabitem.
Code (Command) in RibbonViewModel which adds a new MemberViewModel to the TabItemsCollection
private void AddSelectedMemberTabItem(object notUsed)
{
_tabViewModel.TabItems.Add(new MemberViewModel{ Header = "Member" }); // OK, this works fine
_tabViewModel.TabItems.Add(SelectedMemberViewModel); // this doesnt work, DP SelectedMemberViewModel is never filled, don't know how to retrieve it from the selected datagrid item.
_addOverview.RaiseCanExecuteChanged();
}
Code to retrieve the selected MemberViewModel from the selected datagriditem (in OverviewViewModel):
public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register("SelectedItem", typeof(MemberViewModel), typeof(OverviewViewModel), new UIPropertyMetadata(OnSelectedMemberPropertyChanged));
public MemberViewModel SelectedItem
{
get { return (MemberViewModel)GetValue(SelectedItemProperty); }
set { SetValue(SelectedItemProperty, value); }
}
private static void OnSelectedMemberPropertyChanged(DependencyObject m, DependencyPropertyChangedEventArgs args)
{
var selectedMember = m.GetValue(SelectedItemProperty) as MemberViewModel;
_ribbonViewModel.SelectedMemberViewModel = selectedMember; // error: cannot access non-static field (_ribbonViewModel) in static contect
}
How can I fill the _ribbonViewModel.SelectedMemberViewModel from the OnSelectedMemberPropertyChanged method on OverviewViewModel? Or am I going the wrong way with this?
This is thet way I had to do it, this way I can pass the selected MemberViewModel from the DataGrid to an other var.
private static void OnSelectedMemberPropertyChanged(DependencyObject m, DependencyPropertyChangedEventArgs args)
{
var b = m as OverviewViewModel;
if (b == null)
return;
var selectedMember = m.GetValue(SelectedItemProperty) as MemberViewModel;
b.selectedMemberChanged(selectedMember);
}
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;
}
}