Im working on gauge control.
I need to redraw everything inside when Size or Padding property is changed.
This is how I deal with Size property changes:
public RoundGauge()
{
this.SizeChanged += delegate
{
ReDrawEverything();
};
InitializeComponent();
}
But there is no PaddingChanged event. What can I do with this?
There is no indeed no "PaddingChanged" event but you could use a DependencyPropertyDescriptor to subscribe to changes to a dependency property:
public partial class RoundGauge : UserControl
{
public RoundGauge()
{
InitializeComponent();
DependencyPropertyDescriptor dpd = DependencyPropertyDescriptor.FromProperty(PaddingProperty, typeof(UserControl));
if (dpd != null)
dpd.AddValueChanged(this, OnPaddingChanged);
}
private void OnPaddingChanged(object sender, EventArgs e)
{
MessageBox.Show("Padding changed!");
}
}
Please refer to the following blog post for more information.
Handling changes to dependency properties in the view: https://blog.magnusmontin.net/2014/03/31/handling-changes-to-dependency-properties/
You can override OnPropertyChanged event:
public partial class RoundGauge : Control
{
public RoundGauge()
{
}
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
if (e.Property == PaddingProperty)
{
Thickness oldPadding = (Thickness)e.OldValue;
Thickness newPadding = (Thickness)e.NewValue;
// ...
}
}
}
Related
I'm making a custom behavior for Telerik's RadGridView.
When this behavior is attached and its PropertyName is set to same property as specified by
DataMemberBinding value of some of the GridViewCheckBoxColumn of the grid, then toggling the checkbox in that column will apply same checkbox state to all selected rows (but only to the same column).
That happens in the ApplyToAllSelected method, namely in gvcb.SetCurrentValue(GridViewCheckBox.IsCheckedProperty, isChecked); line. The visuals are working as expected, and all checkbox values are updated on screen.
The Problem is that the binding source is not updated for those rows. Only for the one where click happened. GridViewCheckBox.IsChecked dependency property does not seem to be bound directly to the datacontext's property, so gvcb.GetBindingExpression(GridViewCheckBox.IsChecked) returns null.
The Question: how to update source after setting checkbox state?
public sealed class CheckAllSelectedBehavior : Behavior<RadGridView>
{
public event EventHandler Toggled;
public string PropertyName { get; set; }
protected override void OnAttached()
{
base.OnAttached();
this.AssociatedObject.PreparingCellForEdit += this.AssociatedObject_PreparedCellForEdit;
this.AssociatedObject.CellEditEnded += this.AssociatedObject_CellEditEnded;
}
protected override void OnDetaching()
{
this.AssociatedObject.PreparingCellForEdit -= this.AssociatedObject_PreparedCellForEdit;
this.AssociatedObject.CellEditEnded -= this.AssociatedObject_CellEditEnded;
base.OnDetaching();
}
private void AssociatedObject_CellEditEnded(object sender, GridViewCellEditEndedEventArgs e)
{
if (e.Cell.Column.UniqueName == this.PropertyName && e.EditingElement is CheckBox cb)
{
cb.Checked -= this.Cb_Checked;
cb.Unchecked -= this.Cb_Unchecked;
}
}
private void AssociatedObject_PreparedCellForEdit(object sender, GridViewPreparingCellForEditEventArgs e)
{
if (e.Column.UniqueName == this.PropertyName && e.EditingElement is CheckBox cb)
{
cb.Checked += this.Cb_Checked;
cb.Unchecked += this.Cb_Unchecked;
}
}
private void Cb_Unchecked(object sender, System.Windows.RoutedEventArgs e)
{
this.ApplyToAllSelected(false);
}
private void Cb_Checked(object sender, System.Windows.RoutedEventArgs e)
{
this.ApplyToAllSelected(true);
}
private void ApplyToAllSelected(bool isChecked)
{
foreach (var item in this.AssociatedObject.SelectedItems)
{
var row = this.AssociatedObject.GetRowForItem(item);
var cell = row.GetCellFromPropertyName(this.PropertyName);
if (cell.Content is GridViewCheckBox gvcb)
{
gvcb.SetCurrentValue(GridViewCheckBox.IsCheckedProperty, isChecked);
}
}
this.Toggled?.Invoke(this, EventArgs.Empty);
}
}
Using reflection to set the value on viewmodel property seems to work. Modify the ApplyToAllSelected method like follows:
private void ApplyToAllSelected(bool isChecked)
{
foreach (var item in this.AssociatedObject.SelectedItems)
{
this.SetProperty(item, isChecked);
}
this.Toggled?.Invoke(this, EventArgs.Empty);
}
private void SetProperty(object target, bool isChecked)
{
var prop = target.GetType().GetProperty(
this.PropertyName,
BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetProperty);
prop.SetValue(target, isChecked);
}
Loads the dataGrid and populates the Datagrid a row of 1'
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
update();
//this.DataContext = this;
}
CricketEvent events = new CricketEvent();
private void update()
{
events.updateList(new CricketEvent[1] { new CricketEvent(){Runs="1"} });
DG1.ItemsSource = events.RunsList;
}
private void DG1_SelectedCellsChanged(object sender, SelectedCellsChangedEventArgs e)
{
Window1 windowToOpen = new Window1();
var selectedUser = this.DG1.SelectedItem;
windowToOpen.Show();
}
}
Main class that loads the OnPropertyChanged I have a List property and string property that calls the OnPropertyChanged but I want the individual "Runs" property to be updated on its own rather than the whole collection.
class CricketEvent : INotifyPropertyChanged
{
private ObservableCollection<CricketEvent> runsList;
public string runs { get; set; }
public CricketEvent(string numofRuns) {
this.Runs = numofRuns;
}
public CricketEvent() { }
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<CricketEvent> RunsList
{
get { return this.runsList; }
set
{
if (value != this.runsList)
{
this.runsList = value;
OnPropertyChanged("RunsList");
}
}
}
public string Runs
{
get { return runs; }
set
{
runs = value;
// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged("Runs");
}
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
public ObservableCollection<CricketEvent> updateList(CricketEvent []events)
{
runsList = new ObservableCollection<CricketEvent>(events.ToList());
return runsList;
}
}
This is the update window that brings up a text box and should change the "1s" In the previous window to whatever is typed into the textbox
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
CricketEvent events = new CricketEvent();
MainWindow main = new MainWindow();
private void Button_Click(object sender, RoutedEventArgs e)
{
events.updateList(new CricketEvent[1] { new CricketEvent(txt1.Text.ToString()) });
main.DG1.ItemsSource = events.RunsList;
}
The Button_Click event in Window1 does not use the instance of MainWindow that is show - it creates a new Window instance (that is not shown) and adds the updated list to the DG1.ItemsSource property. To solve that, pass the original instance of Window to the created Window1 in constructor and use that.
However, you should review your update strategy (and code style) because there is potential for improvments:
It is not a good idea to create a new collection if you want to update just one property of one item. Observable collections provide change notification, so you dont have to recreate the collection at all.
Instead of assinging the collection in code behind, use databinding to bind the collection to the ItemsSource. DataBinding results in automatic update of GUI elements if the collection or one item of you collection changed.
I created a UserControl,then customize a routedEvent.Currently,I got a Problem,When first Time init,it raise Invalid,but else time could be work ok.
I was sure that event raised at init,but raised invalid,and no exception Messages.
UserControl Code below :
BaseDictionaryUpDownCheckListBox.xaml.cs
static BaseDictionaryUpDownCheckListBox()
{
//customize a routedEvent
SelectedItemsChangedEvent =
EventManager.RegisterRoutedEvent("SelectedItemsChanged", RoutingStrategy.Bubble,
typeof (EventHandler<RoutedEventArgs>)
, typeof (BaseDictionaryUpDownCheckListBox));
}
//customize a routedEvent
public static readonly RoutedEvent SelectedItemsChangedEvent;
public event RoutedEventHandler SelectedItemsChanged
{
add { this.AddHandler(BaseDictionaryUpDownCheckListBox.SelectedItemsChangedEvent, value); }
remove { this.RemoveHandler(BaseDictionaryUpDownCheckListBox.SelectedItemsChangedEvent, value); }
}
public BaseDictionaryUpDownCheckListBox()
{
BaseDictionaryUpDownCheckListBoxViewModel = new BaseDictionaryUpDownCheckListBoxViewModel();
InitializeComponent();
LayoutRoot.DataContext = BaseDictionaryUpDownCheckListBoxViewModel;
BaseDictionaryUpDownCheckListBoxViewModel.PropertyChanged += PropertyChangedByDetailViewModel;
}
private void PropertyChangedByDetailViewModel(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "SelectedItems")
{
var detailViewModel = sender as BaseDictionaryUpDownCheckListBoxViewModel;
if (detailViewModel != null)
{
SelectedItems = detailViewModel.SelectedItems;
RoutedEventArgs routedArgs =
new RoutedEventArgs(BaseDictionaryUpDownCheckListBox.SelectedItemsChangedEvent, this);
//I was sure that event raised at init,but raised invalid,and no exception Messages
base.RaiseEvent(routedArgs);
}
}
}
Can anyone help?
thanks.
If have an own user control with a DependencyProperty and the corresponding callback method like below:
public partial class PieChart : UserControl
{
public static readonly DependencyProperty RatesProperty = DependencyProperty.Register("Rates", typeof(ObservableCollection<double>), typeof(PieChart),
new PropertyMetadata(new ObservableCollection<double>() { 1.0, 1.0 }, new PropertyChangedCallback(OnRatesChanged)));
[...]
public static void OnRatesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((PieChart)d).drawChart();
}
When using this user control, I bind an ObservableCollection called "Rates" to the RatesProperty. Rates and the related methods look like this:
private ObservableCollection<double> rates;
public ObservableCollection<double> Rates
{
get { return this.rates; }
set
{
if (this.rates != value)
{
this.rates = value;
OnPropertyChanged("Rates");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
When I change the ObservableCollection Rates (e.g. with this.Rates = new ObservableCollection<double>() { 1.0, 2.0 }) the OnRatesChanged() method of the user-control is called like expected. But when I execute the following, it is not called:
this.Rates[0] = (double)1;
this.Rates[1] = (double)2;
OnPropertyChanged("Rates");
I expected that when I raise the PropertyChanged event with the correct property name, the corresponding callback in the user control is always called. Is that wrong?
I found this thread: Getting PropertyChangedCallback of a dependency property always - Silverlight
which covers silverlight but I think then that the same is true in WPF.
So the Framework in the background checks if the bound property (in my example "Rates") changed and only if it changed, it calls the associated callback, correct? Thus changing the elements of my collection has no effect, I always have to change the complete collection?
Thank you!
Your conclusion is right, your OnRatesChanged callback will only be called when the Rates dependency property is set to a new collection (or null).
In order to get notified about changes in the collection, you would also have to register a NotifyCollectionChangedEventHandler:
private static void OnRatesChanged(
DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var pieChart = (PieChart)d;
var oldRates = e.OldValue as INotifyCollectionChanged;
var newRates = e.NewValue as INotifyCollectionChanged;
if (oldRates != null)
{
oldRates.CollectionChanged -= pieChart.OnRatesCollectionChanged;
}
if (newRates != null)
{
newRates.CollectionChanged += pieChart.OnRatesCollectionChanged;
}
pieChart.drawChart();
}
private void OnRatesCollectionChanged(
object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
...
}
drawChart();
}
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...]
}
}