I have a UserControl that has an ObservableCollection dependency property with a property changed callback. The callback rebuilds the nodes in a TreeView control. This all works fine but I would like to be able to have design data. Unfortunately, neither the constructor, the callback nor a default value function call is called by the designer unless I embed my control in another. Is there a way loading default data in this scenario?
Below is the code behind for my control
public partial class ScheduleResourcesSummaryTreeView : UserControl
{
public ObservableCollection<PerformanceProjection> SelectedPerformances
{
get { return (ObservableCollection<PerformanceProjection>)GetValue(SelectedPerformancesProperty); }
set { SetValue(SelectedPerformancesProperty, value); }
}
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SelectedPerformancesProperty =
DependencyProperty.Register("SelectedPerformances",
typeof(ObservableCollection<PerformanceProjection>),
typeof(ScheduleResourcesSummaryTreeView),
new FrameworkPropertyMetadata(
new ObservableCollection<PerformanceProjection>(),
FrameworkPropertyMetadataOptions.AffectsRender, SelectedPerformancesChanged)
);
private static void SelectedPerformancesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (!(d is ScheduleResourcesSummaryTreeView resourcesTree)) return;
resourcesTree.ResourcesTree.Items.Clear();
var sessions = e.NewValue as ObservableCollection<PerformanceProjection> ?? new ObservableCollection<PerformanceProjection>();
if (sessions.Count == 0) return;
var projections = sessions.SelectMany(x => x.ResourceBookingProjections);
TreeNode<ResourceBookingProjection> resourceBookingTreeRoot = new RvtSummaryTree(new ObservableCollection<ResourceBookingProjection>(projections), "");
foreach (var treeNode in resourceBookingTreeRoot.Children)
{
resourcesTree.ResourcesTree.Items.Add((TreeNode<ResourceBookingProjection>)treeNode);
}
}
private static ObservableCollection<PerformanceProjection> DefaultCollection()
{
var prop = DesignerProperties.IsInDesignModeProperty;
var designMode = (bool) DependencyPropertyDescriptor
.FromProperty(prop, typeof(FrameworkElement))
.Metadata.DefaultValue;
if (!designMode)
return new ObservableCollection<PerformanceProjection>();
var designdata = new PerformanceProjectionsViewModelDesignData();
return designdata.SelectedPerformances;
}
public ScheduleResourcesSummaryTreeView()
{
InitializeComponent();
}
}
Related
Problem: I have a ListBox with TextBlocks whos Text property are bound to different properties. I wish to drag the TextBlock onto a OxyPlot and have the plot create a new LineSeries with a collection that should be bound to the same binding as for the TextBlock (is this making sense?)
I have derived a class from TextBlock to handle the OnMouseMove event like this:
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (CanDrag && (e.LeftButton == MouseButtonState.Pressed))
{
// Make sure we have a data binding
BindingExpression binding = GetBindingExpression(TextProperty);
if(binding == null)
{ return; }
// Package the data.
DataObject data = new DataObject();
data.SetData("DragListText.Binding", binding);
// Inititate the drag-and-drop operation.
DragDrop.DoDragDrop(this, data, DragDropEffects.Copy);
}
}
Also I have derived a class from Oxy.Plot that handles the OnDrop:
protected override void OnDrop(DragEventArgs e)
{
base.OnDrop(e);
// DataObject must contain a DragListText.Binding object
if (e.Data.GetDataPresent("DragListText.Binding"))
{
BindingExpression binding = e.Data.GetData("DragListText.Binding") as BindingExpression;
AddSeries(binding);
}
e.Handled = true;
}
The AddSeries function does the following:
public void AddSeries(BindingExpression binding)
{
plot1 = new PlotCollection();
LineSeries newSeries = new LineSeries();
newSeries.ItemsSource = plot1.Collection;
Series.Add(newSeries);
}
And lastly the PlotCollection is defined as:
public class PlotCollection : DependencyObject
{
public ObservableCollection<DataPoint> Collection;
public static DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(double), typeof(PlotCollection), new PropertyMetadata(0.0, new PropertyChangedCallback(OnValueChanged)));
private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{ ((PlotCollection)d).AddLast(); }
public double Value
{
get { return (double)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
public PlotCollection()
{
Collection = new ObservableCollection<DataPoint>();
}
protected void AddLast()
{
Collection.Add(new DataPoint(OxyPlot.Axes.DateTimeAxis.ToDouble(DateTime.Now), Value));
}
}
So my question is this: How do I create a binding on PlotCollection.Value that matches the one from the TextBlock.Text?
In your AddSeries method, try adding this line of code:
BindingOperations.SetBinding(plot1, PlotCollection.ValueProperty, binding.ParentBinding);
Found out the problem,
I needed to add a PropertyChangedCallback to the ValueProperty declaration, like this:
public static DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(double), typeof(DynamicSeries), new PropertyMetadata(0.0, OnValueChanged));
And then handle the property changes in the callback method:
private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
PlotCollection ds = (PlotCollection)d;
ds.AppendValue((double)e.NewValue);
}
I guess that I have misunderstood how the Value property works?!
Thanks for taking the time to try and help me...
I have a simple search text box. This text box acts as a filter. I've now copied/pasted the code for the 5th time and enough is enough. Time for a custom control.
left and right brackets have been replaced with ()
My custom control will be simple. My problem is I want to have a dependencyProperty on this control that is of type List(T).
I created a test project to proof it out and make sure it works. It works well. Ignore List.
Below is the entire class. The problem is that the only thing holding me up is replacing List (Person) with List(T). Something like List where: T is Object
typeof(List(T) where: T is Object) <= Obviously I can't do that but gives an idea what I'm trying to accomplish.
public class SearchTextBox : TextBox
{
public static readonly DependencyProperty SourceProperty =
DependencyProperty.Register("FilterSource", typeof(List<Person>), typeof(SearchTextBox), new UIPropertyMetadata(null)); //I WANT THIS TO BE LIST<T>
public List<Person> FilterSource
{
get
{
return (List<Person>)GetValue(SourceProperty);
}
set
{
SetValue(SourceProperty, value);
}
}
public static readonly DependencyProperty FilterPropertyNameProperty =
DependencyProperty.Register("FilterPropertyName", typeof(String), typeof(SearchTextBox), new UIPropertyMetadata());
public String FilterPropertyName
{
get
{
return (String)GetValue(FilterPropertyNameProperty);
}
set
{
SetValue(FilterPropertyNameProperty, value);
}
}
public SearchTextBox()
{
this.KeyUp += new System.Windows.Input.KeyEventHandler(SearchBox_KeyUp);
}
void SearchBox_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)
{
ICollectionView view = CollectionViewSource.GetDefaultView(FilterSource);
view.Filter = null;
view.Filter = new Predicate<object>(FilterTheSource);
}
bool FilterTheSource(object obj)
{
if (obj == null) return false;
Type t = obj.GetType();
PropertyInfo pi = t.GetProperty(FilterPropertyName);
//object o = obj.GetType().GetProperty(FilterPropertyName);
String propertyValue = obj.GetType().GetProperty(FilterPropertyName).GetValue(obj, null).ToString().ToLower();
if (propertyValue.Contains(this.Text.ToLower()))
{
return true;
}
return false;
}
}
No; that's not possible.
Instead, just use the non-generic typeof(IList).
I have a two dependency properties(both List of strings) in a custom user control.The binding for one of these dependency properties can be changed several times for the life of the application. I need to do some action in the user control when the binding is changed, and I need to access all the dependency properties in the class for doing the action.
For example,
public class UC:UserControl
{
public List<string> AvailableItems
{
get { return (List<string>)this.GetValue(AvailableItemsProperty); }
set { this.SetValue(AvailableItemsProperty, value); }
}
public static readonly DependencyProperty AvailableItemsProperty = DependencyProperty.Register(
"AvailableItems", typeof(List<string>), typeof(ItemSelectionUserControl), new FrameworkPropertyMetadata(OnAvailableItemsChanged) { BindsTwoWayByDefault = true });
public List<string> SelectedItems
{
get { return (List<string>)this.GetValue(SelectedItemsProperty); }
set { this.SetValue(SelectedItemsProperty, value); }
}
public static readonly DependencyProperty SelectedItemsProperty = DependencyProperty.Register(
"SelectedItems", typeof(List<string>), typeof(ItemSelectionUserControl), new FrameworkPropertyMetadata { BindsTwoWayByDefault = true });
public static void OnAvailableItemsChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
//How to access SelectedItems here??
}
}
The trouble is the the callback when dependency property changed should be static, so how can I access the non static dependency property wrapper in the function?? Or is there any other way to do this??
Use the following:
public static void OnAvailableItemsChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
UC uc = sender as UC;
List<string> selectedItems = uc.SelectedItems;
}
Recently i was developing a custom control in Silverlight, I created custom dependency property which is of type ObservableCollection. I have another 2 custom dependency properties of type strings. My requirement is on addition of any item to collection, I have to fire collectionChanged Event , in this event handler, i want to update the other 2 dependency properties.
public static readonly DependencyProperty itemsProperty = DependencyProperty.Register("Items", typeof(ObservableCollection<ValidationErrorMessage>), typeof(SummaryUserControl), new PropertyMetadata(new ObservableCollection<ValidationErrorMessage>(), new PropertyChangedCallback(fun1)));
public ObservableCollection<ValidationErrorMessage> Items
{
get
{
return (ObservableCollection<ValidationErrorMessage>)base.GetValue(itemsProperty);
}
set
{
base.SetValue(itemsProperty, value);
}
}
public static void fun1(object sender, DependencyPropertyChangedEventArgs evt)
{
var newValue = evt.NewValue as ObservableCollection<ValidationErrorMessage>;
if(newValue!=null)
newValue.CollectionChanged += new NotifyCollectionChangedEventHandler(CollectionChangedHandler);
var oldValue = evt.OldValue as ObservableCollection<ValidationErrorMessage>;
if(oldValue!=null)
oldValue.CollectionChanged -= new NotifyCollectionChangedEventHandler(CollectionChangedHandler);
}
static void CollectionChangedHandler(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
var newItems = e.NewItems as ObservableCollection<ValidationErrorMessage>;
foreach (var item in newItems)
{
item.PropertyChanged += new PropertyChangedEventHandler(item_PropertyChanged);
}
}
}
static void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
}
public static readonly DependencyProperty headerProperty = DependencyProperty.Register("Header", typeof(String), typeof(SummaryUserControl), new PropertyMetadata(String.Empty, null));
public String Header
{
get
{
return (String)base.GetValue(headerProperty);
}
set
{
base.SetValue(headerProperty, value);
RaisePropertyChange("Header");
}
}
public static readonly DependencyProperty messageTypeProperty =
DependencyProperty.Register("MessageType", typeof(MessageEnumType), typeof(SummaryUserControl), new PropertyMetadata(MessageEnumType.Error, null));
public MessageEnumType MessageType
{
get { return (MessageEnumType)GetValue(messageTypeProperty); }
set { SetValue(messageTypeProperty, value); RaisePropertyChange("MessageType"); }
}
How can I change the values of the dependency properties messageType and Header? I'm unable to access those properties in either the CollectionChanged or NotifyPropertyChanged event since all those events are static. I cannot access the instance within these static event handlers.
I tried to fix the problem with a converter, but my curosity on Silverlight makes me want to use the above approach. How can I set values for those dependency properties within CollectionChanged event or NotifyPropertyChanged events?
The sender in your static fun1 method should be the instance of the class which declares the itemsProperty DependencyProperty. Therefore you can access the concrete instance with casting the sender to your class.
public static void fun1(object sender, DependencyPropertyChangedEventArgs evt)
{
MyClass concreteInstance = sender as MyClass;
if(concreateInstance != null)
{
[...your code...]
}
}
I have a class called MyComponent and it has a DependencyProperty caled BackgroundProperty.
public class MyComponent
{
public MyBackground Background
{
get { return (MyBackground)GetValue(BackgroundProperty); }
set { SetValue(BackgroundProperty, value); }
}
public static readonly DependencyProperty BackgroundProperty =
DependencyProperty.Register("Background", typeof(MyBackground),
typeof(MyComponent), new FrameworkPropertyMetadata(default(MyBackground), new PropertyChangedCallback(OnPropertyChanged)));
}
MyBackground is a class that derives from DependencyObject and it has some DependencyProperties.
public class MyBackground : DependencyObject
{
public Color BaseColor
{
set { SetValue(BaseColorProperty, value); }
get { return (Color)GetValue(BaseColorProperty); }
}
public static readonly DependencyProperty BaseColorProperty =
DependencyProperty.Register("BaseColor", typeof(Color),
typeof(MyBackground ), new UIPropertyMetadata(Colors.White));
[...]
}
Now, what I want is when a property from MyBackground is changed, MyComponent to be notified that MyBackground has changed and the PropertyChangedCallback named OnPropertyChanged to be called.
Bear with me for a second because it appears that you are trying to go against the grain of WPF. Since it seems you are writing code related to display logic, the typical method for getting related DependencyObjects to interact with one another is through bindings.
If, for example, MyComponent is a control of some sort and it uses the Background property in its ControlTemplate, you would use a TemplateBinding that references the Background property and any important sub-properties.
Since 1) you probably already know that and 2) you either aren't using templates or don't have them available, you can set up a binding in code in order to react to changes in to the Background property. If you provide more detail about what your OnPropertyChanged method does I can provide some sample code.
One way to do what you describe would be to derive from Freezable instead of DependencyObject. When a property of a Freezable changes the PropertyChangedCallback for any DO referencing that Freezable will be invoked so the callback for the Background property of your MyComponent. In that case the e.OldValue and e.NewValue will be the same reference. Internally WPF has some flag on the event args that indicates that it is a subobject change.
This is what the framework does for things like brushes so that an element can be invalidated if say the Color property of a SolidColorBrush is changed. If an object will never be changed (or you want to make it thread safe) then one can freezing the object (i.e. making it immutable).
BTW I would probably avoid using Background as the name of the property. Most developers will assume that is of type Brush as that is what the framework uses for that named property on several of its elements (e.g. control, border).
Sounds like you want to use a DependencyPropertyDescriptor and AddValueChanged.
Here's an article on it: http://www.codeproject.com/Articles/34741/Change-Notification-for-Dependency-Properties.aspx
..and possibly a better implementation: http://agsmith.wordpress.com/2008/04/07/propertydescriptor-addvaluechanged-alternative/
Here's a small static class of extension methods I wrote for WPF -- it allows you to register an EventHandler or an Action callback for the changing of any DependencyProperty on any DependencyObject. No changes to the dependency object are necessary.
It also prevents recursion (i.e. if you change that same property during the callback, etc..)
It takes advantage of the DependencyPropertyDescriptor that #ScottBilas linked to.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows;
namespace BrainSlugs83.Writes.Too.Much.Code
{
public static class WpfExtensions
{
public static void OnPropertyChanged<T>(this T obj, DependencyProperty prop, Action<T> callback) where T : DependencyObject
{
if (callback != null)
{
obj.OnPropertyChanged(prop, new EventHandler((o, e) =>
{
callback((T)o);
}));
}
}
public static void OnPropertyChanged<T>(this T obj, DependencyProperty prop, EventHandler handler) where T : DependencyObject
{
var descriptor = DependencyPropertyDescriptor.FromProperty(prop, typeof(T));
descriptor.AddValueChanged(obj, new EventHandler((o, e) =>
{
if (handler != null)
{
if (o == null) { handler(o, e); }
else
{
lock (PreventRecursions)
{
if (IsRecursing(obj, prop)) { return; }
SetIsRecursing(obj, prop, true);
}
try
{
handler(o, e);
}
finally
{
SetIsRecursing(obj, prop, false);
}
}
}
}));
}
#region OnPropertyChanged Recursion Prevention
private static readonly Dictionary<object, List<DependencyProperty>> PreventRecursions = new Dictionary<object, List<DependencyProperty>>();
private static bool IsRecursing(object obj, DependencyProperty prop)
{
lock (PreventRecursions)
{
List<DependencyProperty> propList = null;
if (PreventRecursions.ContainsKey(obj))
{
propList = PreventRecursions[obj];
}
return propList == null ? false : propList.Contains(prop);
}
}
private static void SetIsRecursing(object obj, DependencyProperty prop, bool value)
{
lock (PreventRecursions)
{
List<DependencyProperty> propList = null;
if (PreventRecursions.ContainsKey(obj))
{
propList = PreventRecursions[obj];
}
if (propList == null)
{
if (!value) { return; }
propList = PreventRecursions[obj] = new List<DependencyProperty>();
}
if (value)
{
if (!propList.Contains(prop))
{
propList.Add(prop);
}
}
else
{
while (propList.Contains(prop))
{
propList.Remove(prop);
}
if (!propList.Any())
{
propList = PreventRecursions[obj] = null;
}
}
}
}
#endregion
public static bool IsInDesignMode(this DependencyObject obj)
{
try
{
return DesignerProperties.GetIsInDesignMode(obj);
}
catch { /* do nothing */ }
return false;
}
}
}