Removing PropertyChanged-item from BindingList causes list to Reset - c#

User defined class inherits from INotifyPropertyChanged.
In user defined class some property broadcasts PropertyChanged event.
During that event object itself is removed from BindingList.
Event continues execution and BindingList gets ListChangedType.Reset event.
What can be done to avoid Reset event ?

(Haven't seen answer to this question, so decided to add both - question and answer)
Google for "BindingList Child_PropertyChanged"
gives following code snipet:
http://referencesource.microsoft.com/#System/compmod/system/componentmodel/BindingList.cs,e757be5fba0e6000,references
Means that if event is received by BindingList, and given item is not in BindingList - it will trigger list reset.
But because PropertyChanged.Invoke collect whole list of events before starting to broadcast before execution - event will be invoked anyway independently of whether item is in list or not.
Let following code snipet to demonstrate error:
Program.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
using System.Reflection;
using System.Security;
namespace Eventing
{
public class ThisItem : MulticastNotifyPropertyChanged
{
void Test()
{
}
String _name;
public String name2
{
get {
return _name;
}
set
{
_name = value;
Console.WriteLine("---------------------------");
Console.WriteLine("Invoke test #2");
Console.WriteLine("---------------------------");
Invoke(this, new PropertyChangedEventArgs("name"));
}
}
public String name1
{
get {
return _name;
}
set
{
_name = value;
#if TESTINVOKE
Console.WriteLine("---------------------------");
Console.WriteLine("Invoke test #1");
Console.WriteLine("---------------------------");
InvokeFast(this, new PropertyChangedEventArgs("name"));
#endif
}
}
};
class Program
{
static public BindingList<ThisItem> testList;
static void Main(string[] args)
{
testList = new BindingList<ThisItem>();
ThisItem t = new ThisItem();
testList.ListChanged += testList_ListChanged;
t.PropertyChanged += t_PropertyChanged;
t.PropertyChanged += t_PropertyChanged2;
testList.Add(t);
t.name1 = "testing";
Console.WriteLine("---------------------------");
t.PropertyChanged -= t_PropertyChanged;
t.PropertyChanged -= t_PropertyChanged2;
t.PropertyChanged += t_PropertyChanged;
testList.Add(t);
t.PropertyChanged += t_PropertyChanged2;
t.name2 = "testing";
}
static void testList_ListChanged(object sender, ListChangedEventArgs e)
{
Console.WriteLine("3) List changed: " + e.ListChangedType.ToString() + ((e.ListChangedType == ListChangedType.Reset) ? " (*** UPS! ***)": ""));
}
static void t_PropertyChanged2(object sender, PropertyChangedEventArgs e)
{
Console.WriteLine("2) t_PropertyChanged2: " + e.PropertyName);
}
static void t_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
Console.WriteLine("1) t_PropertyChanged: " + e.PropertyName);
testList.Remove((ThisItem)sender);
}
}
}
MulticastNotifyPropertyChanged.cs:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace System
{
/// <summary>
/// class which implements INotifyPropertyChanged in such manner that event can be broadcasted in safe manner -
/// even if given item is removed from BindingList, event in BindingList (Child_PropertyChanged) won't be
/// triggered.
/// </summary>
public class MulticastNotifyPropertyChanged : INotifyPropertyChanged
{
/// <summary>
/// List of all registered events. List can change during event broadcasting.
/// </summary>
List<PropertyChangedEventHandler> _PropertyChangedHandlers = new List<PropertyChangedEventHandler>();
/// <summary>
/// Next broadcasted event index.
/// </summary>
int iFuncToInvoke;
#if TESTINVOKE
PropertyChangedEventHandler _PropertyChangedAllInOne;
#endif
event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
{
add
{
#if TESTINVOKE
_PropertyChangedAllInOne += value;
#endif
_PropertyChangedHandlers.Add(value);
}
remove
{
#if TESTINVOKE
_PropertyChangedAllInOne -= value;
#endif
int index = _PropertyChangedHandlers.IndexOf(value);
if (index == -1)
return;
if (iFuncToInvoke >= index) //Scroll back event iterator if needed.
iFuncToInvoke--;
#if TESTINVOKE
Console.WriteLine("Unregistering event. Iterator value: " + iFuncToInvoke.ToString());
#endif
_PropertyChangedHandlers.Remove(value);
}
}
/// <summary>
/// Just an accessor, so no cast would be required on client side.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged
{
add
{
((INotifyPropertyChanged)this).PropertyChanged += value;
}
remove
{
((INotifyPropertyChanged)this).PropertyChanged -= value;
}
}
/// <summary>
/// Same as normal Invoke, except this plays out safe - if item is removed from BindingList during event broadcast -
/// event won't be fired in removed item direction.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void Invoke(object sender, PropertyChangedEventArgs e)
{
for (iFuncToInvoke = 0; iFuncToInvoke < _PropertyChangedHandlers.Count; iFuncToInvoke++)
{
#if TESTINVOKE
Console.WriteLine("Invoke: " + iFuncToInvoke.ToString());
#endif
_PropertyChangedHandlers[iFuncToInvoke].Invoke(sender, e);
}
}
#if TESTINVOKE
public void InvokeFast( object sender, PropertyChangedEventArgs e )
{
_PropertyChangedAllInOne.Invoke(sender, e);
}
#endif
}
} //namespace System
Will result in following execution flow:
3) List changed: ItemAdded
---------------------------
Invoke test #1
---------------------------
1) t_PropertyChanged: name
Unregistering event. Iterator value: 0
3) List changed: ItemDeleted
2) t_PropertyChanged2: name
3) List changed: Reset (*** UPS! ***)
---------------------------
Unregistering event. Iterator value: -1
Unregistering event. Iterator value: -1
3) List changed: ItemAdded
---------------------------
Invoke test #2
---------------------------
Invoke: 0
1) t_PropertyChanged: name
Unregistering event. Iterator value: 0
3) List changed: ItemDeleted
Invoke: 1
2) t_PropertyChanged2: name
This solved list eventing - however it can create more problems with items adding to BindingList durign event. This code can probably we fixed also to not to broadcast events to newly added items (like normal BindingList does), but this is something you can work out on your own if needed.

Related

WPF global event handler in App.xaml.cs

Hi we would to handler the event OnPropertyChanged and gets the value in all application forms of this variable.
using System;
using System.ComponentModel;
using System.Windows;
public partial class App : INotifyPropertyChanged
{
#region - Connected -
/// <summary>
/// Gets or sets Connected status
/// </summary>
private Boolean connected = false;
public Boolean Connected
{
get { return connected; }
set
{
if(connected != value)
{
connected = value;
OnPropertyChanged("Connected");
}
}
}
#endregion - Connected -
#region - INotifyPropertyChanged implementation -
// Basically, the UI thread subscribes to this event and update the binding if the received Property Name correspond to the Binding Path element
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion - INotifyPropertyChanged implementation -
}
how can fired this event "OnPropertyChanged" and get the value Connected On all App's windows.
On the surface, this looks as simple as each form calling
(Application.Current as App).PropertyChanged += ....
And in your handler, use
(sender as App).Connected
to get the value of that property.

ProperyChanged event always null - Windows Phone

I have seen some answers related to this issue but neither quite resembled this scenario I have. I have a settings class and when one of the properties is updated I want another class to be notified. The event is being fired but however its handler is always null.
Can somebody help me understand what is wrong?
class AppSettings : INotifyPropertyChanged
{
// Our settings
ApplicationDataContainer settings ;
// The key names of our settings
const string CheckBoxSettingKeyName = "CheckBoxSetting";
const string ComboBoxSettingKeyName = "ComboBoxSetting";
// The default value of our settings
const bool CheckBoxSettingDefault = true;
private int ComboBoxSettingDefault = 0;
public event PropertyChangedEventHandler PropertyChanged;
public AppSettings()
{
settings = ApplicationData.Current.LocalSettings;
}
public bool AddOrUpdateValue(string Key, Object value)
{
bool valueChanged = false;
// If the key exists
if (settings.Values.ContainsKey(Key))
{
// If the value has changed
if (settings.Values[Key] != value)
{
// Store the new value
settings.Values[Key] = value;
valueChanged = true;
}
}
// Otherwise create the key.
else
{
settings.Values.Add(Key, value);
valueChanged = true;
}
return valueChanged;
}
/// <summary>
/// Get the current value of the setting, or if it is not found, set the
/// setting to the default setting.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="Key"></param>
/// <param name="defaultValue"></param>
/// <returns></returns>
public T GetValueOrDefault<T>(string Key, T defaultValue)
{
T value;
// If the key exists, retrieve the value.
if (settings.Values.ContainsKey(Key))
{
value = (T)settings.Values[Key];
}
// Otherwise, use the default value.
else
{
value = defaultValue;
}
return value;
}
/// Property to get and set a ComboBox Setting Key.
public int ComboBoxSettings
{
get
{
return GetValueOrDefault<int>(ComboBoxSettingKeyName, ComboBoxSettingDefault);
}
set
{
AddOrUpdateValue(ComboBoxSettingKeyName, value);
NotifyPropertyChanged();
}
}
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
I removed some of your code and created a new console application. Then I changed to Program class to:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
using System.ComponentModel;
using System.Runtime.CompilerServices;
class Program
{
static void Main(string[] args)
{
// create instance
var settings = new AppSettings();
// subscribe for the event as soon as you can
settings.PropertyChanged += (s, e) => Console.WriteLine("Property {0} has changed", e.PropertyName);
Console.WriteLine("Press any key to start test");
Console.ReadKey();
// change the value
settings.ComboBoxSettings = 10;
Console.ReadKey();
}
}
class AppSettings : INotifyPropertyChanged
{
private int _comboBoxSettings;
public event PropertyChangedEventHandler PropertyChanged;
public int ComboBoxSettings
{
get
{
return _comboBoxSettings;
}
set
{
_comboBoxSettings = value;
NotifyPropertyChanged();
}
}
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
}
If you past this into a fresh console application it works.

Bind value from one class to another value in another class

I have the following classes gist with the classes.
I want to bind Item.Visible to Items.ItemsVisible - is it possible?, if so - how?
Item.cs:
using System;
using System.ComponentModel;
namespace WpfApplication85
{
/// <summary>
/// Item Object.
/// </summary>
public class Item : INotifyPropertyChanged
{
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged; //Event to notify when Property changed.
/// <summary>
/// Notify that Property has Changed.
/// </summary>
/// <param name="propertyName">The name of the Property</param>
protected void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
#region Private Variables
private bool _Visible; //Bool to determine if the Item is visible or not
#endregion
#region Public Properties
//Return the value of Visible / Set the value of Visible and Notify.
public bool Visible
{
get { return _Visible; }
set
{
_Visible = value;
NotifyPropertyChanged("Visible");
}
}
#endregion
#region Constructor
/// <summary>
/// Item Constructor
/// </summary>
public Item()
{
_Visible = true;
}
#endregion
}
}
Items.cs:
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
namespace WpfApplication85
{
/// <summary>
/// Items Object.
/// </summary>
public class Items : INotifyPropertyChanged
{
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged; //Event to notify when Property changed.
/// <summary>
/// Notify that Property has Changed.
/// </summary>
/// <param name="propertyName">The name of the Property</param>
protected void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
#region Private Variables
private bool _itemsVisible; //Bool to determine if the Items are visible or not
private ObservableCollection<Item> _itemsCollection; //Collection of Items.
#endregion
#region Public Properties
//Return the value of ItemsVisible / Set the value of ItemsVisible and Notify.
public bool ItemsVisible
{
get { return _itemsVisible; }
set
{
_itemsVisible = value;
NotifyPropertyChanged("ItemsVisible");
}
}
//Return the Items Collection / Set the Items Collection and Notify.
public ObservableCollection<Item> ItemsCollection
{
get
{
return _itemsCollection;
}
set
{
_itemsCollection = value;
NotifyPropertyChanged("ItemsCollection");
}
}
#endregion
#region Constructor
/// <summary>
/// Items Constructor
/// </summary>
public Items()
{
_itemsVisible = true;
_itemsCollection = new ObservableCollection<Item>();
}
#endregion
#region Methods
/// <summary>
/// Add Item to the ItemsCollection.
/// </summary>
/// <param name="item">Item Object</param>
public void AddItem(Item item)
{
//Bind item.Visible to this.ItemsVisible
_itemsCollection.Add(item);
}
#endregion
}
}
Setting data binding within Items and Item properties is nothing but listening PropertyChanged or CollectionChanged event from proper interfaces.
You can use either += clause for the subscription, or WeakEventListener pattern, using the PropertyChangedEventManager and CollectionChangedEventManager
I prefer the last one, because:
Listening for events can lead to memory leaks.
So, your Items class should implement IWeakEventListener interface:
public class Items : INotifyPropertyChanged, IWeakEventListener
{
#region IWeakEventListener
public bool ReceiveWeakEvent(Type managerType, Object sender, EventArgs e)
{
if (sender == this._itemsCollection && managerType == typeof(CollectionChangedEventManager))
{
// Your collection has changed, you should add/remove
// subscription for PropertyChanged event
UpdateSubscriptions((NotifyCollectionChangedEventArgs)e);
return true;
}
if (sender is Item && managerType == typeof(PropertyChangedEventManager))
{
// The Visible property of an Item object has changed
// You should handle it properly here, for example, like this:
this.ItemsVisible = this._itemsCollection.All(i => i.Visible);
return true;
}
return false;
}
private void UpdateSubscriptions(NotifyCollectionChangedEventArgs e)
{
switch(e.Action)
{
case NotifyCollectionChangedAction.Add:
foreach (Item item in e.NewItems)
{
PropertyChangedEventManager.AddListener(item, this, "Visible");
}
break;
case NotifyCollectionChangedAction.Remove:
foreach (Item item in e.OldItems)
{
PropertyChangedEventManager.RemoveListener(item, this, "Visible");
}
break;
case NotifyCollectionChangedAction.Reset:
foreach (Item item in this._itemsCollection)
{
PropertyChangedEventManager.RemoveListener(item, this, "Visible");
PropertyChangedEventManager.AddListener(item, this, "Visible");
}
break;
default:
break;
}
}
...
public Items()
{
_itemsVisible = true;
_itemsCollection = new ObservableCollection<Item>();
CollectionChangedEventManager.AddListener(_itemsCollection, this);
}
}
Binding in the WPF sense only works on DependencyProperties, and does not apply between two standard properties (even when using INotifyPropertyChanged).
That said, if you are using these classes as View Models and are binding them to controls you could use a MultiConverter to set the visibility of the Control to collapsed when both the ItemsVisible and Visible property are true (for example).
Alternatively, you could add a Parent property to the Item class and set it to the parent Items class which would allow you to have the Item.Visible property return the parent's ItemsVisible property (or again whatever logic makes sense in your application).

Unable to remove cast event handler

I have a design pattern (not sure if this is a commonly used DP, if someone has a name for it please let me know) where I have have a non-generic and a generic interface for a class. The implementation stores generic values and implements the generics interface implicitly. It also explicitly implements the non-generic interface with each property returning the value of the generic property suitably cast to its non-generic form. This works really well for properties, but I am having a few issues getting it to work quite as nicely for events.
Below is a greatly simplified version of what I am doing. The idea is that adding handlers to either interfaces version of Event should add it to the same event so that when the event fires it doesn't matter how it was subscribed to. The test code in Main shows that the event handler is not being removed as I would expect. What is the correct code for adding to/removing from Event using INormalInterface.Event's add/remove blocks?
class Program
{
static void Main(string[] args)
{
INormalInterface x = new ImplementingClass<int>();
Console.WriteLine("Created x and invoking...");
x.InvokeEvent();
Console.WriteLine("Adding event and invoking...");
x.Event += x_Event;
x.InvokeEvent();
Console.WriteLine("Removing event and invoking...");
x.Event -= x_Event;
x.InvokeEvent();
Console.WriteLine("Done.");
Console.ReadKey(true);
}
static void x_Event(object sender, NormalEventArgs e)
{
Console.WriteLine("Event Handled!");
}
}
interface INormalInterface
{
event EventHandler<NormalEventArgs> Event;
void InvokeEvent();
}
interface IGenericInterface<T> : INormalInterface
{
new event EventHandler<GenericEventArgs<T>> Event;
}
class ImplementingClass<T> : IGenericInterface<T>
{
public event EventHandler<GenericEventArgs<T>> Event;
event EventHandler<NormalEventArgs> INormalInterface.Event
{
add { Event += new EventHandler<GenericEventArgs<T>>(value); }
remove { Event -= new EventHandler<GenericEventArgs<T>>(value); }
}
public void InvokeEvent()
{
if (Event != null)
{
Event(this, new GenericEventArgs<T>());
}
}
}
class NormalEventArgs : EventArgs
{
}
class GenericEventArgs<T> : NormalEventArgs
{
}
I figure the issue is because I am 'new'ing the delegate each time so it won't resolve to the same value when adding/removing, is there a way to cast delegates? I do have one solution but it requires having a field for each event, so would appreciate any solutions that avoids this:
class ImplementingClass<T> : IGenericInterface<T>
{
private readonly Dictionary<EventHandler<NormalEventArgs>, EventHandler<GenericEventArgs<T>>> m_eventDictionary = new Dictionary<EventHandler<NormalEventArgs>, EventHandler<GenericEventArgs<T>>>();
public event EventHandler<GenericEventArgs<T>> Event;
event EventHandler<NormalEventArgs> INormalInterface.Event
{
add { Event += m_eventDictionary[value] = new EventHandler<GenericEventArgs<T>>(value); }
remove { Event -= m_eventDictionary[value]; }
}
public void InvokeEvent()
{
if (Event != null)
{
Event(this, new GenericEventArgs<T>());
}
}
}
This does the trick, but I wouldn't call it pretty:
event EventHandler<NormalEventArgs> INormalInterface.Event
{
add
{
var handler = (EventHandler<GenericEventArgs<T>>)Delegate.CreateDelegate(typeof(EventHandler<GenericEventArgs<T>>), value.Target, value.Method);
Event += handler;
}
remove
{
var handler = (EventHandler<GenericEventArgs<T>>)Delegate.CreateDelegate(typeof(EventHandler<GenericEventArgs<T>>), value.Target, value.Method);
Event -= handler;
}
}
The issue with
add { Event += new EventHandler<GenericEventArgs<T>>(value); }
is that it creates a delegate for the Delegate.Invoke method, so it cannot find a match in the event's multicast delegate. Is that, and not the creation of a new object itself, that prevents you from removing the handler.
New Answer
Not the prettiest, but this seems to do the trick:
event EventHandler<NormalEventArgs> INormalInterface.Event
{
add { Event += new EventHandler<GenericEventArgs<T>>(value); }
remove
{
var d = Event.GetInvocationList().First(x => x.Target.GetHashCode() == value.GetHashCode());
Event -= (EventHandler<GenericEventArgs<T>>) d;
}
}
Original Answer:
It seems to me that you have your interfaces around the wrong way - unless you have an existing reason for it, I would change it to be like this:
class Program
{
static void Main(string[] args)
{
IGenericInterface<int> x = new ImplementingClass<int>();
Console.WriteLine("Created x and invoking...");
x.InvokeEvent();
Console.WriteLine("Adding event and invoking...");
x.Event += x_Event;
x.InvokeEvent();
Console.WriteLine("Removing event and invoking...");
x.Event -= x_Event;
x.InvokeEvent();
Console.WriteLine("Done.");
Console.ReadKey(true);
}
static void x_Event(object sender, NormalEventArgs e)
{
Console.WriteLine("Event Handled!");
}
}
interface IBaseInterface<T> where T : EventArgs
{
event EventHandler<T> Event;
void InvokeEvent();
}
interface INormalInterface : IBaseInterface<NormalEventArgs>
{
}
interface IGenericInterface<T> : IBaseInterface<GenericEventArgs<T>>
{
}
class ImplementingClass<T> : IGenericInterface<T>
{
public event EventHandler<GenericEventArgs<T>> Event;
public void InvokeEvent()
{
if (Event != null)
{
Event(this, new GenericEventArgs<T>());
}
}
}
class NormalEventArgs : EventArgs
{
}
class GenericEventArgs<T> : NormalEventArgs
{
}
I think that the observer pattern could be used here http://www.dofactory.com/Patterns/PatternObserver.aspx
using System;
using System.Collections.Generic;
namespace DoFactory.GangOfFour.Observer.RealWorld
{
/// <summary>
/// MainApp startup class for Real-World
/// Observer Design Pattern.
/// </summary>
class MainApp
{
/// <summary>
/// Entry point into console application.
/// </summary>
static void Main()
{
// Create IBM stock and attach investors
IBM ibm = new IBM("IBM", 120.00);
ibm.Attach(new Investor("Sorros"));
ibm.Attach(new Investor("Berkshire"));
// Fluctuating prices will notify investors
ibm.Price = 120.10;
ibm.Price = 121.00;
ibm.Price = 120.50;
ibm.Price = 120.75;
// Wait for user
Console.ReadKey();
}
}
/// <summary>
/// The 'Subject' abstract class
/// </summary>
abstract class Stock
{
private string _symbol;
private double _price;
private List<IInvestor> _investors = new List<IInvestor>();
// Constructor
public Stock(string symbol, double price)
{
this._symbol = symbol;
this._price = price;
}
public void Attach(IInvestor investor)
{
_investors.Add(investor);
}
public void Detach(IInvestor investor)
{
_investors.Remove(investor);
}
public void Notify()
{
foreach (IInvestor investor in _investors)
{
investor.Update(this);
}
Console.WriteLine("");
}
// Gets or sets the price
public double Price
{
get { return _price; }
set
{
if (_price != value)
{
_price = value;
Notify();
}
}
}
// Gets the symbol
public string Symbol
{
get { return _symbol; }
}
}
/// <summary>
/// The 'ConcreteSubject' class
/// </summary>
class IBM : Stock
{
// Constructor
public IBM(string symbol, double price)
: base(symbol, price)
{
}
}
/// <summary>
/// The 'Observer' interface
/// </summary>
interface IInvestor
{
void Update(Stock stock);
}
/// <summary>
/// The 'ConcreteObserver' class
/// </summary>
class Investor : IInvestor
{
private string _name;
private Stock _stock;
// Constructor
public Investor(string name)
{
this._name = name;
}
public void Update(Stock stock)
{
Console.WriteLine("Notified {0} of {1}'s " +
"change to {2:C}", _name, stock.Symbol, stock.Price);
}
// Gets or sets the stock
public Stock Stock
{
get { return _stock; }
set { _stock = value; }
}
}
}

Static function as eventhandler in xaml

I am using this code to simulate the tab functionality in my silverlight application.
I would really like to avoid writing the function a lot of times because it has to be used on quite a lot of textboxes throughout the application. I have created a static class
public static class TabInsert
{
private const string Tab = " ";
public static void textBox_KeyDown(object sender, KeyEventArgs e)
{
TextBox textBox = sender as TextBox;
if (e.Key == Key.Tab)
{
int selectionStart = textBox.SelectionStart;
textBox.Text = String.Format("{0}{1}{2}",
textBox.Text.Substring(0, textBox.SelectionStart),
Tab,
textBox.Text.Substring(textBox.SelectionStart + textBox.SelectionLength, (textBox.Text.Length) - (textBox.SelectionStart + textBox.SelectionLength))
);
e.Handled = true;
textBox.SelectionStart = selectionStart + Tab.Length;
}
}
}
so that I can access it from various places liek this textBox.KeyDown += TabInsert.textBox_KeyDown;
Is there a way I can do this in XAML?
You can create a Behavior (System.Windows.Interactivity namespace) to easily attach to textboxes that in the OnAttached() override subscribes to the event and does the handling as you do and unsubscribes in OnDetaching().
Something like:
public class TabInsertBehavior : Behavior<TextBox>
{
/// <summary>
/// Called after the behavior is attached to an AssociatedObject.
/// </summary>
/// <remarks>
/// Override this to hook up functionality to the AssociatedObject.
/// </remarks>
protected override void OnAttached()
{
base.OnAttached();
this.AssociatedObject.KeyDown += textBox_KeyDown;
}
private const string Tab = " ";
public static void textBox_KeyDown(object sender, KeyEventArgs e)
{
TextBox textBox = sender as TextBox;
if (e.Key == Key.Tab)
{
int selectionStart = textBox.SelectionStart;
textBox.Text = String.Format("{0}{1}{2}",
textBox.Text.Substring(0, textBox.SelectionStart),
Tab,
textBox.Text.Substring(textBox.SelectionStart + textBox.SelectionLength, (textBox.Text.Length) - (textBox.SelectionStart + textBox.SelectionLength))
);
e.Handled = true;
textBox.SelectionStart = selectionStart + Tab.Length;
}
}
/// <summary>
/// Called when the behavior is being detached from its AssociatedObject, but before it has actually occurred.
/// </summary>
/// <remarks>
/// Override this to unhook functionality from the AssociatedObject.
/// </remarks>
protected override void OnDetaching()
{
base.OnDetaching();
this.AssociatedObject.KeyDown -= textBox_KeyDown;
}
}
Unfortunately, there is no direct way to do this in XAML.The event handlers you write in the code behind must be instance methods and cannot be static methods. These methods must be defined by the partial class within the CLR namespace identified by x:Class. You cannot qualify the name of an event handler to instruct a XAML processor to look for an event handler for event wiring in a different class scope.

Categories