I realize my own Calendar.
I did a Generic.xaml (ResourceDictionnary) which contains my new control. I have a Calendar.class who implement :Control.
In my Calendar class I have a ObservableCollection<Day> _days. I put DataContext = this;
A Day contains my ObservableCollection<MyObject> ListeTache.
And Day.Class Implement INotifyPropertyChanged and have my event :
public event PropertyChangedEventHandler PropertyChanged;
But when I update my Listbox, I have to reload my calendar manually to see any changes.
Am I missing something ?
Thank you for the help.
My ObservableCollection<MyObject> :
public ObservableCollection<Tache> ListeTache
{
get { return this._listeTache; }
set
{
_listeTache = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("ListeTache"));
}
}
}
My Generic.xaml look like this :
<Grid x:Name="LayoutTache">
<ListBox x:Name="ListeTaches" ItemsSource="{Binding ListeTache,UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" FontSize="10" PreviewMouseDown="PreviewMouseDownClick_clear" MouseDoubleClick="doubleClic" IsSynchronizedWithCurrentItem="False">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding AffichageCalendrier}"></TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.Resources>
<ContextMenu x:Key="MonMenu">
<MenuItem Header="Supprimer" Click="MonMenuDel_Click" />
</ContextMenu>
</ListBox.Resources>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="AntiqueWhite"></Setter>
<Setter Property="ContextMenu" Value="{StaticResource MonMenu}"/>
</Trigger>
</Style.Triggers>
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="LightGreen" />
</Style.Resources>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
</Grid>
After somme reply :
How I can do that ? I have to add something like this in my Day.cs class :
_listeTache.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(_listeTache_CollectionChanged);
void _listeTache_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
throw new NotImplementedException();
}
I never catch the event...
Thanks for all
When designing a custom control, it is customary not to set the DataContext to this... in fact, don't set it to anything as this enables it to be set from outside the control. Instead, you should reference your property from generic.xaml using a RelativeSource Binding:
<ListBox x:Name="ListeTaches" ItemsSource="{Binding ListeTache, RelativeSource={
RelativeSource AncestorType={x:Type YourXamlNamespacePrefix:Calendar}}}" ... />
It should also be noted that using UpdateSourceTrigger=PropertyChanged, Mode=TwoWay on an ItemsSource Binding is pointless as the ItemsControl cannot update the source collection.
If you still can't access the property, then you must either ensure that you correctly implement the INotifyPropertyChanged interface in Calendar.cs, or you can implement your ListeTaches property as a DependencyProperty instead.
UPDATE >>>
You've clearly done something wrong... it's really not that complicated. Follow the links that I provided to declare a DependencyProperty in your Calendar.cs class. Do not set the DataContext. Use the RelativeSource Binding that I showed you, correctly setting up the proper XAML Namespace... that's it!
Just one last thing... you did add a WPF Custom Control Library project into your application, didn't you? You need to have something like this in your Calendar class' static constructor:
static Calendar()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(Calendar),
new FrameworkPropertyMetadata(typeof(Calendar)));
}
Perhaps it would help if you read through the Control Authoring Overview page on MSDN to ensure that you are doing it correctly?
UPDATE 2 >>>
Ok, so after noticing something in your comments, I think that I know what your problem is. #XAMlMAX asked you
have you tried removing that null check for your PropertyChanged in Listtache?
You replied
When I remove it, I catch TargetInvocationException.
I think that that's your problem... that means that your PropertyChanged event is null... that means that you have not attached a handler to it yet... it's not being used. Try attaching a handler to it and your ListeTache collection should display fine.
Related
Whenever a node is selected in my treeview, it automatically does a horizontal scroll to that item. I've found the way to disable this. If I use this code in code behind, it works perfectly:
<TreeView>
<TreeView.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<EventSetter Event="RequestBringIntoView" Handler="TreeViewItem_RequestBringIntoView"/>
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
private void TreeViewItem_RequestBringIntoView(object sender, RequestBringIntoViewEventArgs e)
{
e.Handled = true;
}
However, if I use MVVM, I cannot disable horizontal scroll to the item:
My window:
<Window x:Class="TreeViewWpfApplication.MainWindow"
. . . . .
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:ei=http://schemas.microsoft.com/expression/2010/interactions>
<TreeView Grid.Column="1" Margin="5" Background="Green">
<i:Interaction.Triggers>
<i:EventTrigger EventName="RequestBringIntoView">
<ei:CallMethodAction MethodName="RequestBringIntoView_Handler" TargetObject="{Binding}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<TreeView>
<TreeViewItem Header="---Level 1" >
<TreeViewItem Header="--- Level 2.1" >
<TreeViewItem Header="--- Level 3.1" >
</TreeViewItem>
</TreeViewItem>
</TreeViewItem>
<TreeViewItem Header="Level 2.3" />
</TreeView>
</Window>
ViewModel:
public void RequestBringIntoView_Handler(object sender, RequestBringIntoViewEventArgs e)
{
e.Handled = true;
}
Why cannot I stop automatic horizontal scroll to the item by MVVM approach?
I believe you can always loop through all TreeViewItem of the TreeView and clone each TriggerBase from Interaction.Triggers attached on the TreeView before attaching each cloned one to the Interaction.Triggers of each TreeViewItem.
I'm so disappointed about Microsoft, that name has made me proud of and been my endless inspiration for many years since I started beginning to learn how to program. But frankly speaking there are many things Microsoft made us disappointed. Your code should have actually worked fine. Why? I've tried it and the event RequestBringIntoView actually bubbles up from TreeViewItem to TreeView. And in fact when you add the event handler on TreeView directly, you'll see the event handler is fired OK. But the very equivalent form of setting handler using Interaction does not work that way. That's so terrible. It's obvious that it's designed to setup event handler in MVVM way but it's so limited.
I had to make a work-around in which we use a custom attached property to allow to set the Interaction.Triggers in Style. However I have to say that it's not very pretty. You need to explicitly declare an Array of TriggerBase (I've done something like this before but never found a better solution for this). Next you need to use a proxy to bind TargetObject for EventTrigger (because we put the trigger in an Array and it's detached from visual tree).
Here is the code for the custom attached property:
//add some using alias like this first
//using i = System.Windows.Interactivity;
public static class InteractionX
{
public static readonly DependencyProperty TriggersProperty
= DependencyProperty.RegisterAttached("Triggers", typeof(i.TriggerBase[]),
typeof(InteractionX), new PropertyMetadata(triggersChanged));
public static i.TriggerBase[] GetTriggers(DependencyObject o){
return o.GetValue(TriggersProperty) as i.TriggerBase[];
}
public static void SetTriggers(DependencyObject o, i.TriggerBase[] value)
{
o.SetValue(TriggersProperty, value);
}
static void triggersChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
var triggers = e.NewValue as i.TriggerBase[];
var currentTriggers = i.Interaction.GetTriggers(o);
currentTriggers.Clear();
foreach (var t in triggers)
{
t.Detach();
currentTriggers.Add(t);
}
}
}
Here is the XAML:
<TreeView>
<TreeView.Resources>
<DiscreteObjectKeyFrame x:Key="proxy" Value="{Binding}"/>
</TreeView.Resources>
<TreeView.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<Setter Property="local:InteractionX.Triggers">
<Setter.Value>
<x:Array Type="{x:Type i:TriggerBase}">
<i:EventTrigger EventName="RequestBringIntoView">
<ei:CallMethodAction MethodName="bringIntoViewHandler"
TargetObject="{Binding Value, Source={StaticResource proxy}}"/>
</i:EventTrigger>
</x:Array>
</Setter.Value>
</Setter>
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
It appears that the Interaction.Triggers set on TreeViewItem can handle bubbling-up RequestBringIntoView from the descendant TreeViewItems but as I said it's a pity that setting that on TreeView does not work.
I was wondering how can I get the "Selected" MenuItem from a Menu.
Basically, I want to get the "Selected" MenuItem so I can sort my ListBox.
Here is my XAML for the Menu.
<Menu>
<MenuItem Header="Sort by" ItemsSource="{Binding SortByOptions}"
*SelectedItem="{Binding GroupBy}"*/>
</Menu>
I Switched my ComboBox with a Menu, but in Menu, "SelectedItem" does not exist like in ComboBox. I was wondering how could I get what Item from menu was chosen.
C#
The ItemsSource Binding "SortByOptions" is an ObservableCollection of string who contains the options to sort.
The binding "GroupBy" is a String that is set each time the user chose another MenuItem.
I am searching to set the variable "GroupBy" every time the user chose another MenuItem.
Before, my ComboBox worked well.
SOLUTION
I needed to specify the style of the property "Command" and "CommandParameter" like this:
<Menu Layout="Text" Margin="10,0,0,0">
<MenuItem Header="Group by" ItemsSource="{Binding GroupByOptions}">
<MenuItem.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command"
Value="{Binding ViewModel.GroupCommand, RelativeSource={RelativeSource AncestorType={x:Type Views:MyView}}}" />
<Setter Property="CommandParameter" Value="{Binding}" />
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
</Menu>
Note that the CommandParameter is the actual "Header" chosen by the user. (This is what I was searching for) I did not know, but when you do {Binding} it takes the actual string.
And in my ViewModel, here is what it looks like:
private ICommand mSortCommand;
//Implement get and set with NotifyPropertyChanged for mSortableList
private ICollectionView mSortableList;
public ICommand SortCommand
{
get { return mSortCommand ?? (mSortCommand = new RelayCommand(SortMyList)); }
}
public void SortMyList(object sortChosen)
{
string chosenSort = sortChosen as string;
CampaignSortableList.SortDescriptions.Clear();
Switch(chosenSort){
"Sort my List"
}
CampaignSortableList.Refresh();
}
It works all fine now.
I am working on a WPF application and doing my best to follow the MVVM architecture. I am using the GalaSoft MVVM light relay command implementation for all my commands and behavior.
Currently, I am trying to learn and understand attached behaviors, and specifically, how to implement an attached behavior for the text blocks in my application.
What I would like to do is have a style that I can apply to select text blocks that would execute some universal command (more on what I mean by "universal" later)
Here is an example of what I want to do.
For the example, I have two windows. Obviously in the real application I will have many more, but this should suit my instructive needs.
I'd like to apply an attached behavior to the text blocks in these windows that will implement a defined behavior.... Code...
Main Window
<Window x:Class="AttachedExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
Height="350"
Width="525"
DataContext="{Binding Source={StaticResource Locator}, Path=Main}">
<StackPanel>
<TextBlock Text="{Binding Path=SomeMainWindowModel.SomeText}" />
</StackPanel>
</Window>
Main Window View Model
public class MainWindowViewModel : BaseViewModel
{
private MainWindowModel _someMainWindowModel = new MainWindowModel();
public MainWindowModel SomeMainWindowModel
{
get
{
return this._someMainWindowModel;
}
set
{
this._someMainWindowModel = value;
this.NotifyPropertyChange("SomeMainWindowModel");
}
}
}
Main Window Model
public class MainWindowModel : BaseModel
{
private string _someText = "Some main text for Stack Overflow!";
public string SomeText
{
get
{
return this._someText;
}
set
{
this._someText = value;
this.NotifyPropertyChange("SomeText");
}
}
}
And now the secondary window....
Secondary Window
<Window x:Class="AttachedExample.SecondaryWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="SecondaryWindow"
Height="300"
Width="300"
DataContext="{Binding Source={StaticResource Locator}, Path=Secondary}">
<StackPanel>
<TextBlock Text="{Binding Path=SomeSecondaryWindowModel.SomeSecondaryText}" />
</StackPanel>
</Window>
Secondary Window View Model
public class SecondaryWindowViewModel : BaseViewModel
{
private SecondaryWindowModel _someSecondaryWindowModel = new SecondaryWindowModel();
public SecondaryWindowModel SomeSecondaryWindowModel
{
get
{
return this._someSecondaryWindowModel;
}
set
{
this._someSecondaryWindowModel = value;
this.NotifyPropertyChange("SomeSecondaryWindowModel");
}
}
}
Secondary Window Model
public class SecondaryWindowModel : BaseModel
{
private string _someSecondaryText = "Some secondary text for Stack Overflow!";
public string SomeSecondaryText
{
get
{
return this._someSecondaryText;
}
set
{
this._someSecondaryText = value;
this.NotifyPropertyChange("SomeSecondaryText");
}
}
}
What I want to do is be able to have one style in a resource dictionary or in the App.xaml that I can apply to each of these text blocks. The style would specify an attached behavior that would execute a method with the argument of the content of the text block, right clicked.
Pseudo Code
*Right Click text block on MainWindow;*
SomeStaticClass.ExecuteSomeStaticCustomMethod(mainWindowTextBlock.Content.ToString());
*Right Click text block on SecondaryWindow;*
SomeStaticClass.ExecuteSomeStaticCustomMethod(secondaryWindowTextBlock.Content.ToString());
That's a whole lot of example code and explanation to describe something that could be accomplished with an event handler in the code behind... but that wouldn't be following the MVVM pattern.
Please remember I am using MVVM light in your replies.
THis might be what you are looking for and is MVVM compliant. I use a style to attach a relay command to the textblock event which is inside a usercontrol, this usercontrol binds to the MVVM_Light Viewmodel and has the datacontext.
<Style x:Key="StyleName" TargetType="{x:Type TextBlock}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBlock}">
<Border x:Name="Bd" SnapsToDevicePixels="true" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}">
<ScrollViewer x:Name="PART_ContentHost" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="PreviewMouseRightButtonDown">
<cmd:EventToCommand Command="{Binding DataContext.PreviewMouseRightButtonDownCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"
CommandParameter="{Binding Name, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TextBox}}}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I removed the extra style properties to make it easier to read. You need to create a command in your view model that is called PreviewMouseRightButtonDownCommand of type string. THen it will get the textblock name as the parameter. You can change the parameter to whatever binding you want. THis way whatever textblock I want to trigger this event command just needs to have its style set to this StyleName.
Hope this helps
I've spent far too much time with this and can't find the mistake. Maybe I'm missing something very obvious or I may have just found a bug in the WPF Element Host for Winforms.
I am binding a ListView to a ObeservableList that lives on my ProductListViewModel.
I'm trying to implement searching for the ListView with the general Idea to just change the ObservableList with a new list that is filtered.
Anyway, the ListView Binding code looks like this:
<ListView ItemsSource="{Binding Path=Products}" SelectedItem="{Binding Path=SelectedItem}" SelectionMode="Single">
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"></Setter>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"></TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
And the ViewModel code is as vanilla as it can get:
private ObservableCollection<ProductViewModel> products;
public ObservableCollection<ProductViewModel> Products
{
get { return products; }
private set
{
if (products != value)
{
products = value;
OnPropertyChanged("Products");
}
}
}
Now the problem here: Once I debug into my OnPropertyChanged method, I can see that there are no subscribers to the PropertyChanged event (it's null), so nothing happens on the UI..
I already tried Mode=TwoWay and other Binding modes, it seems I can't get the ListView to subscribe to the ItemsSource...
Can anyone help me with this? I'm just about to forget about the ElemenHost and just do it in Winforms
greetings Daniel
Is there any binding error in the output window?
By the way, you should consider getting the collection view wrapping your products, and then filtering the view, instead of replacing the whole collection.
The code would be something like:
var collectionView = CollectionViewSource.GetDefaultView(Products);
collectionView.Filter += item => ...;
I feel like I'm missing a fairly fundamental concept to WPF when it comes to databinding, but I can't seem to find the right combination of Google keywords to locate what I'm after, so maybe the SO Community can help. :)
I've got a WPF usercontrol that needs to databind to two separate objects in order to display properly. Both objects must be dynamically set from an outside source. Thus far I've simply been using the DataContext property of the form for dynamic object binding, but that only allows for one object to be referenced. I feel like this is a simple problem and that I must be missing something obvious.
My previous attempt looks something like this:
<UserControl.Resources>
<src:Person x:Key="personSource" />
<src:Job x:Key="jobSource" />
</UserControl.Resources>
<TextBox Text="{Binding Source={StaticResource personSource}, Path=Name" />
<TextBox Text="{Binding Source={StaticResource jobSource}, Path=Address" />
This will bind to any defaults I give the classes just fine, but If I try to dynamically set the objects in code (as I show below) I don't see any change.
Person personSource = FindResource("personSource") as Person;
personSource = externalPerson;
Job jobSource= FindResource("jobSource") as Job;
jobSource = externalJob;
What am I missing?
I would probably use a CustomControl with two DependencyProperties. Then the external site that uses your custom control could bind the data that they want to that control, also by using a custom control you can template the way the control looks in different situations.
Custom control code would look something like:
public class CustomControl : Control
{
public static readonly DependencyProperty PersonProperty =
DependencyProperty.Register("Person", typeof(Person), typeof(CustomControl), new UIPropertyMetadata(null));
public Person Person
{
get { return (Person) GetValue(PersonProperty); }
set { SetValue(PersonProperty, value); }
}
public static readonly DependencyProperty JobProperty =
DependencyProperty.Register("Job", typeof(Job), typeof(CustomControl), new UIPropertyMetadata(null));
public Job Job
{
get { return (Job) GetValue(JobProperty); }
set { SetValue(JobProperty, value); }
}
static CustomControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControl), new FrameworkPropertyMetadata(typeof(CustomControl)));
}
}
Generic.xaml is a file that should be created for you and could have a Style that looks something like this:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication3">
<Style TargetType="{x:Type local:CustomControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CustomControl}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<StackPanel>
<TextBox Text="{TemplateBinding Person.Name}" />
<TextBox Text="{TemplateBinding Job.Address}" />
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
Finally, when you go to use your control you would do something like this.
<src:CustomControl Person="{Binding Person}" Job="{Binding Job}" />
The reason your text boxes don't update is that you are binding them to a StaticResource. As the name implies these resources are static and don't post change notifications. And because Binding is a MarkupExtension and does not derive from DependencyObject you can't use a DynamicResource.
Try creating depedency properties on your control to reference the Person and Job objects.
Then set the DataContext of the UserControl to reference itself.
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Then you can use dot notation to reference the required properties.
<TextBox Text="{Binding Path=Person.Name" />
<TextBox Text="{Binding Path=Job.Address" />
Or use the source parameter
<TextBox Text="{Binding Source=Person, Path=Name" />
<TextBox Text="{Binding Source=Job, Path=Address" />