Implementing an observer pattern with winforms - c#

I have one collection of objects that many of my forms (using WeifenLuo.WinFormsUI.Docking) need to interact with.
i.e. If the collection has an addition (or deletion) made in one form, then the other forms respond by refreshing their views.
Obviously, an observer pattern would be a good candidate here. However, I am having issues trying to implement this in my program.
First, it seemed best to create an observer class for my collection like so:
public class DataCollectionObserver : Form
{
internal static void DataCollectionRegister(DataCollection dataCollection)
{
dataCollection.ImageAdded += new EventHandler(dataAdded);
dataCollection.ImageRemoved += new EventHandler(dataRemoved);
dataCollection.ImageIndexChanged += new EventHandler(dataIndexChanged);
dataCollection.ImageListCleared += new EventHandler(dataListCleared);
}
internal static void DataCollectionUnRegister(DataCollection dataCollection)
{
dataCollection.ImageAdded -= new EventHandler(dataAdded);
dataCollection.ImageRemoved -= new EventHandler(dataRemoved);
dataCollection.ImageIndexChanged -= new EventHandler(dataIndexChanged);
dataCollection.ImageListCleared -= new EventHandler(dataListCleared);
}
internal static void dataAdded(object sender, EventArgs e) {}
internal static void dataRemoved(object sender, EventArgs e) {}
internal static void dataIndexChanged(object sender, EventArgs e) {}
internal static void dataListCleared(object sender, EventArgs e) {}
}
Then override the base event handlers in the subclassed forms?
But, I cannot do this and use the WeifenLuo.WinFormsUI.Docking library...
Well, I could have DataCollectionObserver inherit DockContent from WeifenLuo.WinFormsUI.Docking, but that creates a situation where I need to have two DataCollectionObserver classes -
one which inherits Form and another that inherits DockContent :-[
or, I could make the DataCollectionObserver an Interface, but that still leaves me with duplicate code laying about...
So, does anyone have a suggestion here? Am I missing something obvious, or is this a situation where duplication of code 'must be done' for the sake of simplicity?
Edit://
I am not having problems getting notification in my forms. As a matter of fact, the whole thing is working right now. The reason that I am asking is because the whole thing "smells" due to the block copy and paste of code in these four different forms that I have which subscribe to the collection events and unsubscribe on Form.Closing().
What I would like to do is implement the behavior that I have copied and pasted to these four forms in one place and have the forms that should receive collection change notifications implement that behavior as needed.
Hope that makes things clearer?
FWIW, this is my collection class:
using System;
using System.Collections;
using System.Reflection;
namespace MyNameSpace.Collections
{
/// <summary>
/// Generic Collection of Objects with Events
/// </summary>
public class CollectionWithEvents<T> : CollectionBase
{
public bool SuppressEventNotification
{
get;
set;
}
public CollectionWithEvents()
{
SuppressEventNotification = false;
}
#region Events
/// <summary>
/// Raises before an item is added to the list.
/// </summary>
public event EventHandler<ItemEventArgs<T>> BeforeItemAdded;
/// <summary>
/// Raises when an item is added to the list.
/// </summary>
public event EventHandler<ItemEventArgs<T>> ItemAdded;
/// <summary>
/// Raises before a collection of items is added to the list.
/// </summary>
public event EventHandler<ItemsEventArgs<T>> BeforeItemsAdded;
/// <summary>
/// Raises when a collection of items is added to the list.
/// </summary>
public event EventHandler<ItemsEventArgs<T>> ItemsAdded;
/// <summary>
/// Raises before an item is changed in the list.
/// </summary>
public event EventHandler<ItemEventArgs<T>> BeforeItemChanged;
/// <summary>
/// Raises when an item is changed in the list.
/// </summary>
public event EventHandler<ItemEventArgs<T>> ItemChanged;
/// <summary>
/// Raises before an item is removed from the list.
/// </summary>
public event EventHandler<ItemEventArgs<T>> BeforeItemRemoved;
/// <summary>
/// Raises when an item is removed from the list.
/// </summary>
public event EventHandler<ItemEventArgs<T>> ItemRemoved;
/// <summary>
/// Raises when the items are cleared from the list.
/// </summary>
public event EventHandler<EventArgs> ItemsCleared;
#endregion
public T this[int index]
{
get { return (T)this.List[index]; }
set
{
if (!SuppressEventNotification)
{
OnBeforeItemChanged(this, new ItemEventArgs<T>(value));
}
this.List[index] = value;
if (!SuppressEventNotification)
{
OnItemChanged(this, new ItemEventArgs<T>(value));
}
}
}
public int Add(T item)
{
if (!SuppressEventNotification)
{
OnBeforeItemAdded(this, new ItemEventArgs<T>(item));
}
int retValue = this.List.Add(item);
if (!SuppressEventNotification)
{
OnItemAdded(this, new ItemEventArgs<T>(item));
}
return retValue;
}
public void AddRange(Collection<T> collection)
{
T[] tmp = new T[collection.Count];
collection.CopyTo(tmp, 0);
AddRange(tmp);
}
public void AddRange(T[] collection)
{
if (!SuppressEventNotification)
{
OnBeforeItemsAdded(this, new ItemsEventArgs<T>(collection));
}
this.AddRange(collection);
if (!SuppressEventNotification)
{
OnItemsAdded(this, new ItemsEventArgs<T>(collection));
}
}
public bool Contains(T item)
{
return this.List.Contains(item);
}
public void CopyTo(Array array, int index)
{
this.List.CopyTo(array, index);
}
public int IndexOf(T item)
{
return this.List.IndexOf(item);
}
public void Insert(int index, T item)
{
this.List.Insert(index, item);
}
public void Remove(T item)
{
if (!SuppressEventNotification)
{
OnBeforeItemRemoved(this, new ItemEventArgs<T>(item));
}
T tmp = (T)item;
this.List.Remove(item);
if (!SuppressEventNotification)
{
OnItemRemoved(this, new ItemEventArgs<T>(tmp));
}
tmp = default(T);
}
public void Sort(string Property, Common.SortOrder Order)
{
Common.GenericComparer genericComparer = new Common.GenericComparer(Property, Order);
this.InnerList.Sort(genericComparer);
}
#region Event Methods
/// <summary>
/// Raised before an Item is added to the list.
/// </summary>
/// <param name="sender">object</param>
/// <param name="e">ItemEventArgs</param>
protected virtual void OnBeforeItemAdded(object sender, ItemEventArgs<T> e)
{
if (BeforeItemAdded != null)
{
BeforeItemAdded(sender, e);
}
}
/// <summary>
/// Raised when an Item is added to the list.
/// </summary>
/// <param name="sender"></param>
/// <param name="e">ItemEventArgs</param>
protected virtual void OnItemAdded(object sender, ItemEventArgs<T> e)
{
if (ItemAdded != null)
{
ItemAdded(sender, e);
}
}
/// <summary>
/// Raised before a collection of Items is added to the list.
/// </summary>
/// <param name="sender">object</param>
/// <param name="e">ItemEventArgs</param>
protected virtual void OnBeforeItemsAdded(object sender, ItemsEventArgs<T> e)
{
if (BeforeItemsAdded != null)
{
BeforeItemsAdded(sender, e);
}
}
/// <summary>
/// Raised when a collection of Items is added to the list.
/// </summary>
/// <param name="sender"></param>
/// <param name="e">ItemEventArgs</param>
protected virtual void OnItemsAdded(object sender, ItemsEventArgs<T> e)
{
if (ItemsAdded != null)
{
ItemsAdded(sender, e);
}
}
/// <summary>
/// Raised before an Item is changed to the list.
/// </summary>
/// <param name="sender"></param>
/// <param name="e">GenericItemEventArgs</param>
protected virtual void OnBeforeItemChanged(object sender, ItemEventArgs<T> e)
{
if (BeforeItemChanged != null)
{
BeforeItemChanged(sender, e);
}
}
/// <summary>
/// Raised when an Item is changed to the list.
/// </summary>
/// <param name="sender"></param>
/// <param name="e">ItemEventArgs</param>
protected virtual void OnItemChanged(object sender, ItemEventArgs<T> e)
{
if (ItemChanged != null)
{
ItemChanged(sender, e);
}
}
/// <summary>
/// Raised before an Item is removed from the list.
/// </summary>
/// <param name="sender"></param>
/// <param name="e">ItemEventArgs</param>
protected virtual void OnBeforeItemRemoved(object sender, ItemEventArgs<T> e)
{
if (BeforeItemRemoved != null)
{
BeforeItemRemoved(sender, e);
}
}
/// <summary>
/// Raised when an Item is removed from the list.
/// </summary>
/// <param name="sender">object</param>
/// <param name="e">ItemEventsArgs</param>
protected virtual void OnItemRemoved(object sender, ItemEventArgs<T> e)
{
if (ItemRemoved != null)
{
ItemRemoved(sender, e);
}
}
/// <summary>
/// Raised when the Items are cleared from this list.
/// </summary>
/// <param name="sender">object</param>
/// <param name="e">EventArgs</param>
protected virtual void OnItemsCleared(object sender, EventArgs e)
{
if (ItemsCleared != null)
{
ItemsCleared(sender, e);
}
}
#endregion
}
public class ItemEventArgs<T> : EventArgs
{
/// <summary>
/// Item
/// </summary>
public T Item { get; private set; }
/// <summary>
/// Default constructor
/// </summary>
/// <param name="Item"></param>
public ItemEventArgs(T Item)
{
this.Item = Item;
}
}
public class ItemsEventArgs<T> : EventArgs
{
/// <summary>
/// Items
/// </summary>
public T[] Items { get; private set; }
/// <summary>
/// Default constructor
/// </summary>
/// <param name="Items"></param>
public ItemsEventArgs(T[] Items)
{
this.Items = Items;
}
}
}

I might be mistaken. But you could do it this way and your inheritance problem would not be present. I'll try to give a simple example :
You collection class could be of this kind :
public class MyCollection
{
IList<string> MyList { get; set; }
public event EventHandler<StringEventArgs> OnAdded;
public event EventHandler<StringEventArgs> OnRemoved;
public MyCollection()
{
MyList = new List<string>();
}
public void Add(string s)
{
MyList.Add(s);
if (OnAdded != null)
OnAdded(this, new StringEventArgs() { StringAddedOrRemoved = s });
}
public void Remove(string s)
{
MyList.Remove(s);
if (OnRemoved != null)
OnRemoved(this, new StringEventArgs() { StringAddedOrRemoved = s });
}
}
Really simple class with two customized EventHandlers :
public class StringEventArgs : EventArgs
{
public string StringAddedOrRemoved;
public override string ToString()
{
return StringAddedOrRemoved;
}
}
Nothing hard to understand here and then based on the three forms you could use your forms this way.
The first one holds two buttons to interact with the collection and creates the two forms which will observe your collection :
public partial class Form1 : Form
{
public static MyCollection collection;
public Form1()
{
InitializeComponent();
collection = new MyCollection();
Form2 form2 = new Form2();
form2.Show();
Form3 form3 = new Form3();
form3.Show();
collection.OnAdded += form2.MyCollectionAdded;
collection.OnRemoved += form2.MyCollectionRemoved;
collection.OnAdded += form3.MyCollectionAdded;
collection.OnRemoved += form3.MyCollectionRemoved;
}
private void Add_Click(object sender, EventArgs e)
{
collection.Add("test add");
}
private void button1_Click(object sender, EventArgs e)
{
collection.Remove("test add");
}
}
The collection gets linked to each of the functions of the forms that will be observed here :
collection.OnAdded += form2.MyCollectionAdded;
collection.OnRemoved += form2.MyCollectionRemoved;
collection.OnAdded += form3.MyCollectionAdded;
collection.OnRemoved += form3.MyCollectionRemoved;
And so we need to implement those forms :
public partial class Form2 : Form
{
public string Name { get; set; }
public bool Flag { get; set; }
public Form2()
{
InitializeComponent();
}
public void MyCollectionAdded(object sender, StringEventArgs e)
{
//Some action
Flag = true;
label1.Text = string.Format("{0} has added {1} to its list, flag={2}", Name, e.StringAddedOrRemoved, Flag);
}
public void MyCollectionRemoved(object sender, StringEventArgs e)
{
//Some action
Flag = false;
label1.Text = string.Format("{0} has removed {1} from its list, flag={2}", Name, e.StringAddedOrRemoved, Flag);
}
}
I am inheriting my stuff from Form but it could be inherited form whatever you want actually. If you want to share some code among different forms, think of helper functions in a static class, or of whatever pattern that might suit your need.
Hope it helps a bit, and that I am not completely out of scope!
[EDIT] Oops didn't see the Edit, sorry pal![/EDIT]

Which version of .net ,you are using if it is >= 3.5 , you can make use of ObservableCollection to achive what ever you are doing

There is an 'ObservableCollection' in the Framework just for this type of thing. See here.

Related

Caliburn.Micro GetAllInstances only returns one viewModel(Caliburn.Micro MVVM)

I've been trying to integrate Caliburn.Micro MVVM framework in a C# WPF project I am in the middle off.
I currently only have three view models:
ShellViewModel - (A Window view with a ContentControl)
AboutViewModel - (A usercontrol view)
ChatViewModel - (Another usercontrol view)
Currently I am trying to use a button at the AboutView that is bind to the 'Chat()' method at the AboutViewModel and should take the user to the ChatView, yet I am testing this with the AboutViewModel. (As seen in the handler)
What I need is that all the Screen/ViewModels to be Singleton and only have one instance and when I try to change page, it returns to an already existant page.
The issue here is that I only have one instance registered when I do IoC.GetAllInstances(), the ShellViewModel and even though I've tried multiple configurations at the bootstrapper, I cannot register my other ViewModels in a way to make their instances "reachable"
I thank you for your time, and here is the code I think it's relevant for the issue:
Here is my bootstrapper:
public class AppBootstrapper : BootstrapperBase
{
private SimpleContainer _container = new SimpleContainer();
public AppBootstrapper()
{
Initialize();
var config = new TypeMappingConfiguration
{
DefaultSubNamespaceForViewModels = "ViewModel",
DefaultSubNamespaceForViews = "View"
};
ViewLocator.ConfigureTypeMappings(config);
Caliburn.Micro.ViewModelLocator.ConfigureTypeMappings(config);
}
protected override void Configure()
{
_container.Singleton<ShellViewModel, ShellViewModel>();
_container.Singleton<IWindowManager, WindowManager>();
//tried registering AboutViewModel in multiple ways
_container.Singleton<AboutViewModel, AboutViewModel>();
_container.RegisterSingleton(typeof(AboutViewModel), null,typeof(AboutViewModel));
/
}
protected override void OnStartup(object sender, StartupEventArgs e)
{
DisplayRootViewFor<ShellViewModel>();
}
protected override object GetInstance(Type service, string key)
{
var instance = _container.GetInstance(service, key);
if (instance != null)
return instance;
throw new InvalidOperationException("Could not locate any instances.");
}
protected override IEnumerable<object> GetAllInstances(Type service)
{
return _container.GetAllInstances(service);
}
protected override void BuildUp(object instance)
{
_container.BuildUp(instance);
}
}
ShellViewModel.cs:
public class ShellViewModel : Conductor<object>, IHandle<NavigationMessage>
{
/// <summary>
/// Caliburn.Micro event aggregator. (Publish/Subscribe pattern)
/// </summary>
public IEventAggregator events = new EventAggregator();
public ShellViewModel()
{
//var aaa = IoC.Get<IEventAggregator>();
events.Subscribe(this);
ActivateItem(new AboutViewModel(events));
}
public void Handle(NavigationMessage message)
{
//var instance = IoC.GetInstance(message.ViewModelType,null);
var instances = IoC.GetAllInstances(null);
foreach(var i in instances)
{
MessageBox.Show(i.ToString());
}
ActivateItem(new AboutViewModel(events));
}
}
And the AboutViewModel.cs:
/// <summary>
/// ViewModel belonging to the AboutView.xaml.
/// </summary>
/// <seealso cref="AboutView.xaml"/>
public class AboutViewModel : Screen, IHandle<NavigationMessage>
{
private readonly IEventAggregator _eventAggregator;
/// <summary>
/// Private container for the 'Version' public property.
/// </summary>
/// <see cref="Version"/>
private string _version;
/// <summary>
/// Property containing a string of the application's current version (e.g.: 0.1.3.45)
/// </summary>
/// <see cref="_version"/>
[JsonIgnore]
public string Version
{
get
{
return _version;
}
set
{
_version = value;
NotifyOfPropertyChange(() => Version);
}
}
/// <summary>
/// Base constructor for the AboutViewModel class.
/// </summary>
public AboutViewModel(IEventAggregator eventAggregator)
{
Logging.Info("Initialize AboutViewModel", this.GetType());
Logging.Debug("Subscribing to the eventAggregator", this.GetType());
_eventAggregator = eventAggregator;
_eventAggregator.Subscribe(this);
_version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
Logging.Debug("Version loaded (" + _version + ")", this.GetType());
}
/// <summary>
/// Boolean method connected to the ChatCommand activates or deactivates based on it's return
/// </summary>
/// <param name="obj">Object of type GotoPageMessage received from the messenger</param>
public bool CanChat(object obj)
{
return true;
}
/// <summary>
/// Method connected to the ChatCommand that sends the user to the 'Chat' view
/// </summary>
/// <param name="obj">Object of type GotoPageMessage received from the messenger</param>
public void Chat(object obj)
{
_eventAggregator.PublishOnUIThread(new NavigationMessage(typeof(AboutViewModel)));
}
public void Handle(NavigationMessage message)
{
//This handle is used only to know how many instances I have active
MessageBox.Show("about");
}
}
Edit 1:
P.S.: I used to have my ShellViewModel as Conductor.Collection.OneActive. Still didn't work. Maybe AllActive may work?...
override the SelectAssemblies method for caliburn micro to locate all the views:
protected override IEnumerable<Assembly> SelectAssemblies()
{
return new[]
{
Assembly.GetExecutingAssembly(), typeof(MainViewModel).Assembly
};
}
more on the bootstrapper here.
I actually found a solution right now, without messing with Assemblies.
I noticed that the ShellViewModel's instance was accesible, and by saving the instance into a object and debugging it, I noticed all the viewModel instances I created were there at the 'Items'.
Basically this was what I did, in the handler that is inside the ShellViewModel:
public void Handle(NavigationMessage message)
{
ShellViewModel Shell = (ShellViewModel)IoC.GetInstance(typeof(ShellViewModel), null);
object Instance = null;
foreach (var item in Shell.Items)
{
if (item.ToString().Contains(message.ViewModelType.ToString()))
Instance = item;
}
object AuxObject = new object();
if (Instance == null)
{
try
{
Instance = Activator.CreateInstance(message.ViewModelType, Shell.events);
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
}
ActivateItem(Instance);
}

PCL WeakEventManager from Reactive extensions disposes event in 3 - 7 minutes

I'm trying to implement WeakEventManager in PCL using the Reactive library.
So the point is that it keeps a weak reference for the subscriber and each time event fires - it gets the delegate of the subscriber and fires that, but if he couldn't get an object from weak reference, then it disposes the link to the delegate.
The problem is that after a short amount of time, the weak reference returns null (but the subscriber is still alive) and after that disposing of the link is being performed. So my question is why this is happening and how to fix that?
Here how it looks like: (Look at the note in the code)
private static IDisposable InternalSubscribeWeakly<TEventPattern, TEvent>(this IObservable<TEventPattern> observable, TEvent Weak_onNext, Action<TEvent, TEventPattern> onNext)
where TEvent : class
{
if (onNext.Target != null)
throw new ArgumentException("onNext must refer to a static method, or else the subscription will still hold a strong reference to target");
// Is the delegate alive?
var Weak_onNextReferance = new WeakReference(Weak_onNext);
//This is a link for that event, so if you want to unsubscribe from event you have to dispose this object
IDisposable subscription = null;
subscription = observable.Subscribe(item =>
{
//So the library keeps weak reference for this object and each time event fired it tries to get that object
var current_onNext = Weak_onNextReferance.Target as TEvent;
if (current_onNext != null)
{
//If the object was found, it uses the delegate that subscriber provided and fires the event
onNext(current_onNext, item);
}
else
{
//If the object is not found it disposes the link
//NOTE: For some reasons after a short amount of time it can't get a reference from the WeakReference, however the subscriber is still alive
subscription.Dispose();
}
});
return subscription;
}
And then here is how I'm subscribing using that manager:
private void NoLeakWindow_Loaded(object sender, RoutedEventArgs e)
{
Loaded -= NoLeakWindow_Loaded;
this.ObserveOn<Window, ElapsedEventHandler, ElapsedEventArgs>(h => (o, s) => h(o, s),
r => MainWindow.EPublisher.EventTimer.Elapsed += r,
r => MainWindow.EPublisher.EventTimer.Elapsed -= r)
.SubscribeWeakly(EventTimer_Elapsed);
this.ObserveOn<Window, ElapsedEventHandler, ElapsedEventArgs>(
h => (o, s) => h(o, s),
r => MainWindow.EPublisher.EventTimer.Elapsed += r,
r => MainWindow.EPublisher.EventTimer.Elapsed -= r)
.SubscribeWeakly(EventTimer_Elapsed2);
}
private void EventTimer_Elapsed(EventPattern<ElapsedEventArgs> e)
{
MessageBox.Show("EventTimer_Elapsed By Timer");
}
private void EventTimer_Elapsed2(EventPattern<ElapsedEventArgs> e)
{
MessageBox.Show("EventTimer2_Elapsed2 By Timer2");
}
And my event publisher:
public class EventPublisher
{
public Timer EventTimer = new Timer(3000);
public Timer EventTimer2 = new Timer(2700);
public event EventHandler<EventArgs> TimeElapsed;
public EventPublisher()
{
EventTimer.Start();
EventTimer2.Start();
}
}
And finally the WeakEventManager class full code:
/// <summary>
/// Static Class that holds the extension methods to handle events using weak references.
/// This way we do not need to worry about unregistered the event handler.
/// </summary>
public static class WeakEventManager
{
/// <summary>
/// Creates Observable for subscribing to it's event
/// </summary>
/// <typeparam name="T">The type of the T.</typeparam>
/// <typeparam name="TDelegate">The type of the T delegate.</typeparam>
/// <typeparam name="TArgs">The type of the T args.</typeparam>
/// <param name="subscriber">The subscriber</param>
/// <param name="converter">The converter.</param>
/// <param name="add">The add</param>
/// <param name="remove">The remove</param>
/// <returns>IObservable</returns>
public static IObservable<EventPattern<TArgs>> ObserveOn<T, TDelegate, TArgs>(this T subscriber, Func<EventHandler<TArgs>, TDelegate> converter, Action<TDelegate> add, Action<TDelegate> remove)
where T : class
{
return Observable.FromEventPattern<TDelegate, TArgs>(
converter,
add,
remove);
}
/// <summary>
/// Subscribe's action to event
/// </summary>
/// <typeparam name="T">The type of the T.</typeparam>
/// <param name="observable">The observable</param>
/// <param name="onNext">The action</param>
/// <returns></returns>
public static IDisposable SubscribeWeakly<T>(this IObservable<T> observable, Action<T> onNext) where T : class
{
IDisposable Result = null;
WeakSubscriberHelper<T> SubscriptionHelper = new WeakSubscriberHelper<T>(observable, ref Result, onNext);
return Result;
}
private class WeakSubscriberHelper<T> where T : class
{
public WeakSubscriberHelper(IObservable<T> observable, ref IDisposable Result, Action<T> eventAction)
{
Result = observable.InternalSubscribeWeakly(eventAction, WeakSubscriberHelper<T>.StaticEventHandler);
}
public static void StaticEventHandler(Action<T> subscriber, T item)
{
subscriber(item);
}
}
private static IDisposable InternalSubscribeWeakly<TEventPattern, TEvent>(this IObservable<TEventPattern> observable, TEvent Weak_onNext, Action<TEvent, TEventPattern> onNext)
where TEvent : class
{
if (onNext.Target != null)
throw new ArgumentException("onNext must refer to a static method, or else the subscription will still hold a strong reference to target");
// Is the delegate alive?
var Weak_onNextReferance = new WeakReference(Weak_onNext);
//This is a link for that event, so if you want to unsubscribe from event you have to dispose this object
IDisposable subscription = null;
subscription = observable.Subscribe(item =>
{
//So the library keeps weak reference for this object and each time event fired it tries to get that object
var current_onNext = Weak_onNextReferance.Target as TEvent;
if (current_onNext != null)
{
//If the object was found, it uses the delegate that subscriber provided and fires the event
onNext(current_onNext, item);
}
else
{
//If the object is not found it disposes the link
//NOTE: For some reasons after a short amount of time it can't get a reference from the WeakReference, however the subscriber is still alive
subscription.Dispose();
}
});
return subscription;
}
public static IDisposable SubscribeWeakly<T, TWeakClass>(this IObservable<T> observable, TWeakClass WeakClass, Action<T> onNext) where T : class where TWeakClass : class
{
IDisposable Result = null;
WeakClassSubscriberHelper<T> SubscriptionHelper = new WeakClassSubscriberHelper<T>(observable, WeakClass, ref Result, onNext);
return Result;
}
private class WeakClassSubscriberHelper<T> where T : class
{
public WeakClassSubscriberHelper(IObservable<T> observable, object WeakClass, ref IDisposable Result, Action<T> eventAction)
{
Result = observable.InternalSubscribeWeaklyToClass(eventAction, WeakClass, WeakClassSubscriberHelper<T>.StaticEventHandler);
}
public static void StaticEventHandler(Action<T> subscriber, T item)
{
subscriber(item);
}
}
private static IDisposable InternalSubscribeWeaklyToClass<TEventPattern, TEvent, TClass>(this IObservable<TEventPattern> observable, TEvent Weak_onNext, TClass WeakClass, Action<TEvent, TEventPattern> onNext)
where TEvent : class where TClass : class
{
if (onNext.Target != null)
throw new ArgumentException("onNext must refer to a static method, or else the subscription will still hold a strong reference to target");
// The class instance could live in a differnt
// place than the eventhandler. If either one is null,
// terminate the subscribtion.
var WeakClassReference = new WeakReference(WeakClass);
var Weak_onNextReferance = new WeakReference(Weak_onNext);
IDisposable subscription = null;
subscription = observable.Subscribe(item =>
{
var currentWeakClass = WeakClassReference.Target as TClass;
var current_onNext = Weak_onNextReferance.Target as TEvent;
if (currentWeakClass != null && current_onNext != null)
{
onNext(current_onNext, item);
}
else
{
subscription.Dispose();
}
});
return subscription;
}
}
I came up with my own solution. So basically I merged both of those implementations into one. So my solution is pretty straightforward and I think there are many ways to improve this.
So here is the solution:
/// <summary>
/// PclWeakEventManager base class
/// </summary>
/// <typeparam name="TEventSource">The type of the event source.</typeparam>
/// <typeparam name="TEventHandler">The type of the event handler.</typeparam>
/// <typeparam name="TEventArgs">The type of the event arguments.</typeparam>
public class PclWeakEventManager<TEventSource, TEventHandler, TEventArgs>
{
static readonly object StaticSource = new object();
/// <summary>
/// Mapping between the target of the delegate (for example a Button) and the handler (EventHandler).
/// Windows Phone needs this, otherwise the event handler gets garbage collected.
/// </summary>
ConditionalWeakTable<object, List<Delegate>> targetToEventHandler = new ConditionalWeakTable<object, List<Delegate>>();
/// <summary>
/// Mapping from the source of the event to the list of handlers. This is a CWT to ensure it does not leak the source of the event.
/// </summary>
ConditionalWeakTable<object, WeakHandlerList> sourceToWeakHandlers = new ConditionalWeakTable<object, WeakHandlerList>();
/// <summary>
/// Singleton instance
/// </summary>
static Lazy<PclWeakEventManager<TEventSource, TEventHandler, TEventArgs>> current =
new Lazy<PclWeakEventManager<TEventSource, TEventHandler, TEventArgs>>(() => new PclWeakEventManager<TEventSource, TEventHandler, TEventArgs>());
/// <summary>
/// Get the singleton instance
/// </summary>
static PclWeakEventManager<TEventSource, TEventHandler, TEventArgs> Current
{
get { return current.Value; }
}
/// <summary>
/// Initializes a new instance of the <see cref="PclWeakEventManager{TEventSource, TEventHandler, TEventArgs}"/> class.
/// Protected to disallow instances of this class and force a subclass.
/// </summary>
protected PclWeakEventManager()
{
}
#region Public static methods
/// <summary>
/// Adds a weak reference to the handler and associates it with the source.
/// </summary>
/// <param name="source">The source.</param>
/// <param name="handler">The handler.</param>
public static void AddHandler(TEventSource source, TEventHandler handler, Func<EventHandler<TEventArgs>, TEventHandler> converter, Action<TEventHandler> add, Action<TEventHandler> remove)
{
if (source == null) throw new ArgumentNullException("source");
if (handler == null) throw new ArgumentNullException("handler");
if (!typeof(TEventHandler).GetTypeInfo().IsSubclassOf(typeof(Delegate)))
{
throw new ArgumentException("Handler must be Delegate type");
}
var observable = Observable.FromEventPattern(converter, add, remove);
Current.PrivateAddHandler(source, observable, handler);
}
/// <summary>
/// Removes the association between the source and the handler.
/// </summary>
/// <param name="source">The source.</param>
/// <param name="handler">The handler.</param>
public static void RemoveHandler(TEventSource source, TEventHandler handler)
{
if (source == null) throw new ArgumentNullException("source");
if (handler == null) throw new ArgumentNullException("handler");
if (!typeof(TEventHandler).GetTypeInfo().IsSubclassOf(typeof(Delegate)))
{
throw new ArgumentException("handler must be Delegate type");
}
Current.PrivateRemoveHandler(source, handler);
}
#endregion
#region Event delivering
/// <summary>
/// Delivers the event to the handlers registered for the source.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="args">The <see cref="TEventArgs"/> instance containing the event data.</param>
public static void DeliverEvent(TEventSource sender, TEventArgs args)
{
Current.PrivateDeliverEvent(sender, args);
}
/// <summary>
/// Override this method to attach to an event.
/// </summary>
/// <param name="source">The source.</param>
protected virtual void StartListening(TEventSource source, IObservable<EventPattern<TEventArgs>> observable, TEventHandler handler)
{
//The handler - proxy should be static, otherwise it will create a strong reference
InternalSubscribeWeakly(observable, source, handler, DeliverEvent);
}
/// <summary>
/// Override this method to detach from an event.
/// </summary>
/// <param name="source">The source.</param>
protected virtual void StopListening(object source)
{
//This method is for future usage
}
/// <summary>
/// Fire the event handler
/// </summary>
/// <param name="sender">Event publisher</param>
/// <param name="args">Event arguments</param>
void PrivateDeliverEvent(object sender, TEventArgs args)
{
object source = sender != null ? sender : StaticSource;
var weakHandlers = default(WeakHandlerList);
bool hasStaleEntries = false;
if (this.sourceToWeakHandlers.TryGetValue(source, out weakHandlers))
{
using (weakHandlers.DeliverActive())
{
hasStaleEntries = weakHandlers.DeliverEvent(source, args);
}
}
if (hasStaleEntries)
{
this.Purge(source);
}
}
#endregion
#region Add weak handler methods
/// <summary>
/// Adds the event handler to WeakTables
/// </summary>
/// <param name="source">The event publisher source</param>
/// <param name="observable">Observable object</param>
/// <param name="handler">The event handler. This is used to create a weak reference</param>
void PrivateAddHandler(TEventSource source, IObservable<EventPattern<TEventArgs>> observable, TEventHandler handler)
{
this.AddWeakHandler(source, observable, handler);
this.AddTargetHandler(handler);
}
/// <summary>
/// Add a weak handler
/// </summary>
/// <param name="source">The event publisher source</param>
/// <param name="observable">Observable object</param>
/// <param name="handler">The event handler. This is used to create a weak reference</param>
void AddWeakHandler(TEventSource source, IObservable<EventPattern<TEventArgs>> observable, TEventHandler handler)
{
WeakHandlerList weakHandlers;
//If for the event source table wasn't created, then it creates a new
if (this.sourceToWeakHandlers.TryGetValue(source, out weakHandlers))
{
// clone list if we are currently delivering an event
if (weakHandlers.IsDeliverActive)
{
weakHandlers = weakHandlers.Clone();
this.sourceToWeakHandlers.Remove(source);
this.sourceToWeakHandlers.Add(source, weakHandlers);
}
weakHandlers.AddWeakHandler(source, handler);
}
else
{
weakHandlers = new WeakHandlerList();
weakHandlers.AddWeakHandler(source, handler);
this.sourceToWeakHandlers.Add(source, weakHandlers);
this.StartListening(source, observable, handler);
}
this.Purge(source);
}
/// <summary>
/// Subscribe to the event
/// </summary>
/// <param name="observable">Observable object</param>
/// <param name="source">The event publisher source</param>
/// <param name="handler">The event handler. This is used to create a weak reference</param>
/// <param name="onNext">Event handler delegate</param>
private static void InternalSubscribeWeakly(IObservable<EventPattern<TEventArgs>> observable, TEventSource source, TEventHandler handler, Action<TEventSource, TEventArgs> onNext)
{
if (onNext.Target != null)
throw new ArgumentException("onNext must refer to a static method, or else the subscription will still hold a strong reference to target");
// Is the delegate alive?
var Weak_onNextReferance = new WeakReference(handler);
//This is a link for that event, so if you want to unsubscribe from event you have to dispose this object
IDisposable subscription = null;
subscription = observable.Subscribe(item =>
{
//Purge handler if the subscriber is not alive
Current.Purge(source);
//So the library keeps weak reference for this object and each time event fired it tries to get that object
var current_onNext = Weak_onNextReferance.Target;
if (current_onNext != null)
{
//If the object was found, it uses the delegate that subscriber provided and fires the event
onNext((TEventSource)item.Sender, item.EventArgs);
}
else
{
//If the object is not found it disposes the link
subscription.Dispose();
Current.sourceToWeakHandlers.Remove(source);
}
});
}
/// <summary>
/// Adds the event handler to the weak event handlers list
/// </summary>
/// <param name="handler">The event handler. This is used to create a weak reference</param>
void AddTargetHandler(TEventHandler handler)
{
var #delegate = handler as Delegate;
object key = #delegate.Target ?? StaticSource;
List<Delegate> delegates;
if (this.targetToEventHandler.TryGetValue(key, out delegates))
{
delegates.Add(#delegate);
}
else
{
delegates = new List<Delegate>();
delegates.Add(#delegate);
this.targetToEventHandler.Add(key, delegates);
}
}
#endregion
#region Remove weak handler methods
/// <summary>
/// Remove the event handler
/// </summary>
/// <param name="source">Event source object</param>
/// <param name="handler">The event handler</param>
void PrivateRemoveHandler(TEventSource source, TEventHandler handler)
{
this.RemoveWeakHandler(source, handler);
this.RemoveTargetHandler(handler);
}
/// <summary>
/// Remove the event handler
/// </summary>
/// <param name="source">Event source object</param>
/// <param name="handler">The event handler</param>
void RemoveWeakHandler(TEventSource source, TEventHandler handler)
{
var weakHandlers = default(WeakHandlerList);
if (this.sourceToWeakHandlers.TryGetValue(source, out weakHandlers))
{
// clone list if we are currently delivering an event
if (weakHandlers.IsDeliverActive)
{
weakHandlers = weakHandlers.Clone();
this.sourceToWeakHandlers.Remove(source);
this.sourceToWeakHandlers.Add(source, weakHandlers);
}
if (weakHandlers.RemoveWeakHandler(source, handler) && weakHandlers.Count == 0)
{
this.sourceToWeakHandlers.Remove(source);
this.StopListening(source);
}
}
}
/// <summary>
/// Remove the handler from weaktable
/// </summary>
/// <param name="handler">The event handler</param>
void RemoveTargetHandler(TEventHandler handler)
{
var #delegate = handler as Delegate;
object key = #delegate.Target ?? StaticSource;
var delegates = default(List<Delegate>);
if (this.targetToEventHandler.TryGetValue(key, out delegates))
{
delegates.Remove(#delegate);
if (delegates.Count == 0)
{
this.targetToEventHandler.Remove(key);
}
}
}
/// <summary>
/// Remove dead handlers
/// </summary>
/// <param name="source">Source object</param>
void Purge(object source)
{
var weakHandlers = default(WeakHandlerList);
if (this.sourceToWeakHandlers.TryGetValue(source, out weakHandlers))
{
if (weakHandlers.IsDeliverActive)
{
weakHandlers = weakHandlers.Clone();
this.sourceToWeakHandlers.Remove(source);
this.sourceToWeakHandlers.Add(source, weakHandlers);
}
else
{
weakHandlers.Purge();
}
}
}
#endregion
#region WeakHandler table helper classes
/// <summary>
/// Weak handler helper class
/// </summary>
internal class WeakHandler
{
WeakReference source;
WeakReference originalHandler;
public bool IsActive
{
get { return this.source != null && this.source.IsAlive && this.originalHandler != null && this.originalHandler.IsAlive; }
}
public TEventHandler Handler
{
get
{
if (this.originalHandler == null)
{
return default(TEventHandler);
}
else
{
return (TEventHandler)this.originalHandler.Target;
}
}
}
public WeakHandler(object source, TEventHandler originalHandler)
{
this.source = new WeakReference(source);
this.originalHandler = new WeakReference(originalHandler);
}
/// <summary>
/// Checks if provided handler is the same
/// </summary>
/// <param name="source"></param>
/// <param name="handler"></param>
/// <returns>True if source.Target is equals to source, otherwise false</returns>
public bool Matches(object source, TEventHandler handler)
{
return this.source != null &&
object.ReferenceEquals(this.source.Target, source) &&
this.originalHandler != null;
}
}
/// <summary>
/// Weak event handler manager
/// </summary>
internal class WeakHandlerList
{
int deliveries = 0;
List<WeakHandler> handlers;
public WeakHandlerList()
{
handlers = new List<WeakHandler>();
}
/// <summary>
/// Adds new weak event handler to the list
/// </summary>
/// <param name="source">The event source</param>
/// <param name="handler">The event handler</param>
public void AddWeakHandler(TEventSource source, TEventHandler handler)
{
WeakHandler handlerSink = new WeakHandler(source, handler);
handlers.Add(handlerSink);
}
/// <summary>
/// Remove weak handler from the list
/// </summary>
/// <param name="source">The event source</param>
/// <param name="handler">The event handler</param>
/// <returns>True if the handler was removed, otherwise false</returns>
public bool RemoveWeakHandler(TEventSource source, TEventHandler handler)
{
foreach (var weakHandler in handlers)
{
if (weakHandler.Matches(source, handler))
{
return handlers.Remove(weakHandler);
}
}
return false;
}
/// <summary>
/// Clones the list
/// </summary>
/// <returns></returns>
public WeakHandlerList Clone()
{
WeakHandlerList newList = new WeakHandlerList();
newList.handlers.AddRange(this.handlers.Where(h => h.IsActive));
return newList;
}
/// <summary>
/// Items count
/// </summary>
public int Count
{
get { return this.handlers.Count; }
}
/// <summary>
/// True if any of the events are still in delivering process
/// </summary>
public bool IsDeliverActive
{
get { return this.deliveries > 0; }
}
public IDisposable DeliverActive()
{
Interlocked.Increment(ref this.deliveries);
return Disposable.Create(() => Interlocked.Decrement(ref this.deliveries));
}
/// <summary>
/// Fire the handler
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
/// <returns></returns>
public virtual bool DeliverEvent(object sender, TEventArgs args)
{
bool hasStaleEntries = false;
foreach (var handler in handlers)
{
if (handler.IsActive)
{
var #delegate = handler.Handler as Delegate;
#delegate.DynamicInvoke(sender, args);
}
else
{
hasStaleEntries = true;
}
}
return hasStaleEntries;
}
/// <summary>
/// Removes dead handlers
/// </summary>
public void Purge()
{
for (int i = handlers.Count - 1; i >= 0; i--)
{
if (!handlers[i].IsActive)
{
handlers.RemoveAt(i);
}
}
}
}
#endregion
}

Is there a simple way to avoid or stop FileSystemWatcher raise event twice in C#?

I want to know what how to avoid or stop FileSystemWatcher raise event twice in C#? I have a solution that will detect everytime if there is newly created xml file from a folder. I test my application using creating xml file using notepad but from the listbox it displays twice.
How can I fix this issue?
Here is my code:
private void fileSystemWatcher1_Created(object sender, System.IO.FileSystemEventArgs e)
{
try
{
fileSystemWatcher1.EnableRaisingEvents = false;
listBox1.Items.Add(e.FullPath);
}
finally
{
fileSystemWatcher1.EnableRaisingEvents = true;
}
}
private void button1_Click(object sender, EventArgs e)
{
DialogResult dialogSelectFolder = folderBrowserDialog1.ShowDialog();
if (dialogSelectFolder.ToString() == "OK")
{
textBox1.Text = folderBrowserDialog1.SelectedPath;
button2.Enabled = true;
}
}
private void button2_Click(object sender, EventArgs e)
{
button1.Enabled = false;
button2.Enabled = false;
button3.Enabled = true;
fileSystemWatcher1.EnableRaisingEvents = true;
fileSystemWatcher1.Path = textBox1.Text;
fileSystemWatcher1.Filter = "*.xml";
}
private void button3_Click(object sender, EventArgs e)
{
button1.Enabled = true;
button3.Enabled = false;
textBox1.Text = "";
fileSystemWatcher1.EnableRaisingEvents = false;
}
}
I have stumbled upon this problem myself twice and i created a class that helps you get only one event at a time. You might also get false events when the file is not in read mode (such as when you copy a file).
You have to create a queue and store all events there and if a time interval passes then raise the appropriate event.
Unfortunately this is not a simple function thus i will include complete code.
using System;
using System.IO;
using System.Timers;
using System.Collections;
using System.ComponentModel;
namespace menelabs.core
{
/// <summary>
/// This class wraps FileSystemEventArgs and RenamedEventArgs objects and detection of duplicate events.
/// </summary>
internal class DelayedEvent
{
private readonly FileSystemEventArgs _args;
/// <summary>
/// Only delayed events that are unique will be fired.
/// </summary>
private bool _delayed;
public DelayedEvent(FileSystemEventArgs args)
{
_delayed = false;
_args = args;
}
public FileSystemEventArgs Args
{
get
{
return _args;
}
}
public bool Delayed
{
get
{
return _delayed;
}
set
{
_delayed = value;
}
}
public virtual bool IsDuplicate(object obj)
{
DelayedEvent delayedEvent = obj as DelayedEvent;
if (delayedEvent == null)
return false; // this is not null so they are different
FileSystemEventArgs eO1 = _args;
RenamedEventArgs reO1 = _args as RenamedEventArgs;
FileSystemEventArgs eO2 = delayedEvent._args;
RenamedEventArgs reO2 = delayedEvent._args as RenamedEventArgs;
// The events are equal only if they are of the same type (reO1 and reO2
// are both null or NOT NULL) and have all properties equal.
// We also eliminate Changed events that follow recent Created events
// because many apps create new files by creating an empty file and then
// they update the file with the file content.
return ((eO1 != null && eO2 != null && eO1.ChangeType == eO2.ChangeType
&& eO1.FullPath == eO2.FullPath && eO1.Name == eO2.Name) &&
((reO1 == null & reO2 == null) || (reO1 != null && reO2 != null &&
reO1.OldFullPath == reO2.OldFullPath && reO1.OldName == reO2.OldName))) ||
(eO1 != null && eO2 != null && eO1.ChangeType == WatcherChangeTypes.Created
&& eO2.ChangeType == WatcherChangeTypes.Changed
&& eO1.FullPath == eO2.FullPath && eO1.Name == eO2.Name);
}
}
/// <summary>
/// This class wraps a FileSystemWatcher object. The class is not derived
/// from FileSystemWatcher because most of the FileSystemWatcher methods
/// are not virtual. The class was designed to resemble FileSystemWatcher class
/// as much as possible so that you can use FileSystemSafeWatcher instead
/// of FileSystemWatcher objects.
/// FileSystemSafeWatcher will capture all events from the FileSystemWatcher object.
/// The captured events will be delayed by at least ConsolidationInterval milliseconds in order
/// to be able to eliminate duplicate events. When duplicate events are found, the last event
/// is droped and the first event is fired (the reverse is not recomended because it could
/// cause some events not be fired at all since the last event will become the first event and
/// it won't fire a if a new similar event arrives imediately afterwards).
/// </summary>
internal class FileSystemSafeWatcher
{
private readonly FileSystemWatcher _fileSystemWatcher;
/// <summary>
/// Lock order is _enterThread, _events.SyncRoot
/// </summary>
private readonly object _enterThread = new object(); // Only one timer event is processed at any given moment
private ArrayList _events;
private Timer _serverTimer;
private int _consolidationInterval = 1000; // milliseconds
#region Delegate to FileSystemWatcher
public FileSystemSafeWatcher()
{
_fileSystemWatcher = new FileSystemWatcher();
Initialize();
}
public FileSystemSafeWatcher(string path)
{
_fileSystemWatcher = new FileSystemWatcher(path);
Initialize();
}
public FileSystemSafeWatcher(string path, string filter)
{
_fileSystemWatcher = new FileSystemWatcher(path, filter);
Initialize();
}
/// <summary>
/// Gets or sets a value indicating whether the component is enabled.
/// </summary>
/// <value>true if the component is enabled; otherwise, false. The default is false. If you are using the component on a designer in Visual Studio 2005, the default is true.</value>
public bool EnableRaisingEvents
{
get
{
return _fileSystemWatcher.EnableRaisingEvents;
}
set
{
_fileSystemWatcher.EnableRaisingEvents = value;
if (value)
{
_serverTimer.Start();
}
else
{
_serverTimer.Stop();
_events.Clear();
}
}
}
/// <summary>
/// Gets or sets the filter string, used to determine what files are monitored in a directory.
/// </summary>
/// <value>The filter string. The default is "*.*" (Watches all files.)</value>
public string Filter
{
get
{
return _fileSystemWatcher.Filter;
}
set
{
_fileSystemWatcher.Filter = value;
}
}
/// <summary>
/// Gets or sets a value indicating whether subdirectories within the specified path should be monitored.
/// </summary>
/// <value>true if you want to monitor subdirectories; otherwise, false. The default is false.</value>
public bool IncludeSubdirectories
{
get
{
return _fileSystemWatcher.IncludeSubdirectories;
}
set
{
_fileSystemWatcher.IncludeSubdirectories = value;
}
}
/// <summary>
/// Gets or sets the size of the internal buffer.
/// </summary>
/// <value>The internal buffer size. The default is 8192 (8K).</value>
public int InternalBufferSize
{
get
{
return _fileSystemWatcher.InternalBufferSize;
}
set
{
_fileSystemWatcher.InternalBufferSize = value;
}
}
/// <summary>
/// Gets or sets the type of changes to watch for.
/// </summary>
/// <value>One of the System.IO.NotifyFilters values. The default is the bitwise OR combination of LastWrite, FileName, and DirectoryName.</value>
/// <exception cref="System.ArgumentException">The value is not a valid bitwise OR combination of the System.IO.NotifyFilters values.</exception>
public NotifyFilters NotifyFilter
{
get
{
return _fileSystemWatcher.NotifyFilter;
}
set
{
_fileSystemWatcher.NotifyFilter = value;
}
}
/// <summary>
/// Gets or sets the path of the directory to watch.
/// </summary>
/// <value>The path to monitor. The default is an empty string ("").</value>
/// <exception cref="System.ArgumentException">The specified path contains wildcard characters.-or- The specified path contains invalid path characters.</exception>
public string Path
{
get
{
return _fileSystemWatcher.Path;
}
set
{
_fileSystemWatcher.Path = value;
}
}
/// <summary>
/// Gets or sets the object used to marshal the event handler calls issued as a result of a directory change.
/// </summary>
/// <value>The System.ComponentModel.ISynchronizeInvoke that represents the object used to marshal the event handler calls issued as a result of a directory change. The default is null.</value>
public ISynchronizeInvoke SynchronizingObject
{
get
{
return _fileSystemWatcher.SynchronizingObject;
}
set
{
_fileSystemWatcher.SynchronizingObject = value;
}
}
/// <summary>
/// Occurs when a file or directory in the specified System.IO.FileSystemWatcher.Path is changed.
/// </summary>
public event FileSystemEventHandler Changed;
/// <summary>
/// Occurs when a file or directory in the specified System.IO.FileSystemWatcher.Path is created.
/// </summary>
public event FileSystemEventHandler Created;
/// <summary>
/// Occurs when a file or directory in the specified System.IO.FileSystemWatcher.Path is deleted.
/// </summary>
public event FileSystemEventHandler Deleted;
/// <summary>
/// Occurs when the internal buffer overflows.
/// </summary>
public event ErrorEventHandler Error;
/// <summary>
/// Occurs when a file or directory in the specified System.IO.FileSystemWatcher.Path is renamed.
/// </summary>
public event RenamedEventHandler Renamed;
/// <summary>
/// Begins the initialization of a System.IO.FileSystemWatcher used on a form or used by another component. The initialization occurs at run time.
/// </summary>
public void BeginInit()
{
_fileSystemWatcher.BeginInit();
}
/// <summary>
/// Releases the unmanaged resources used by the System.IO.FileSystemWatcher and optionally releases the managed resources.
/// </summary>
public void Dispose()
{
Uninitialize();
}
/// <summary>
/// Ends the initialization of a System.IO.FileSystemWatcher used on a form or used by another component. The initialization occurs at run time.
/// </summary>
public void EndInit()
{
_fileSystemWatcher.EndInit();
}
/// <summary>
/// Raises the System.IO.FileSystemWatcher.Changed event.
/// </summary>
/// <param name="e">A System.IO.FileSystemEventArgs that contains the event data.</param>
protected void OnChanged(FileSystemEventArgs e)
{
if (Changed != null)
Changed(this, e);
}
/// <summary>
/// Raises the System.IO.FileSystemWatcher.Created event.
/// </summary>
/// <param name="e">A System.IO.FileSystemEventArgs that contains the event data.</param>
protected void OnCreated(FileSystemEventArgs e)
{
if (Created != null)
Created(this, e);
}
/// <summary>
/// Raises the System.IO.FileSystemWatcher.Deleted event.
/// </summary>
/// <param name="e">A System.IO.FileSystemEventArgs that contains the event data.</param>
protected void OnDeleted(FileSystemEventArgs e)
{
if (Deleted != null)
Deleted(this, e);
}
/// <summary>
/// Raises the System.IO.FileSystemWatcher.Error event.
/// </summary>
/// <param name="e">An System.IO.ErrorEventArgs that contains the event data.</param>
protected void OnError(ErrorEventArgs e)
{
if (Error != null)
Error(this, e);
}
/// <summary>
/// Raises the System.IO.FileSystemWatcher.Renamed event.
/// </summary>
/// <param name="e">A System.IO.RenamedEventArgs that contains the event data.</param>
protected void OnRenamed(RenamedEventArgs e)
{
if (Renamed != null)
Renamed(this, e);
}
/// <summary>
/// A synchronous method that returns a structure that contains specific information on the change that occurred, given the type of change you want to monitor.
/// </summary>
/// <param name="changeType">The System.IO.WatcherChangeTypes to watch for.</param>
/// <returns>A System.IO.WaitForChangedResult that contains specific information on the change that occurred.</returns>
public WaitForChangedResult WaitForChanged(WatcherChangeTypes changeType)
{
//TODO
throw new NotImplementedException();
}
/// <summary>
/// A synchronous method that returns a structure that contains specific information on the change that occurred, given the type of change you want to monitor
/// and the time (in milliseconds) to wait before timing out.
/// </summary>
/// <param name="changeType">The System.IO.WatcherChangeTypes to watch for.</param>
/// <param name="timeout">The time (in milliseconds) to wait before timing out.</param>
/// <returns>A System.IO.WaitForChangedResult that contains specific information on the change that occurred.</returns>
public WaitForChangedResult WaitForChanged(WatcherChangeTypes changeType, int timeout)
{
//TODO
throw new NotImplementedException();
}
#endregion
#region Implementation
private void Initialize()
{
_events = ArrayList.Synchronized(new ArrayList(32));
_fileSystemWatcher.Changed += new FileSystemEventHandler(this.FileSystemEventHandler);
_fileSystemWatcher.Created += new FileSystemEventHandler(this.FileSystemEventHandler);
_fileSystemWatcher.Deleted += new FileSystemEventHandler(this.FileSystemEventHandler);
_fileSystemWatcher.Error += new ErrorEventHandler(this.ErrorEventHandler);
_fileSystemWatcher.Renamed += new RenamedEventHandler(this.RenamedEventHandler);
_serverTimer = new Timer(_consolidationInterval);
_serverTimer.Elapsed += new ElapsedEventHandler(this.ElapsedEventHandler);
_serverTimer.AutoReset = true;
_serverTimer.Enabled = _fileSystemWatcher.EnableRaisingEvents;
}
private void Uninitialize()
{
if (_fileSystemWatcher != null)
_fileSystemWatcher.Dispose();
if (_serverTimer != null)
_serverTimer.Dispose();
}
private void FileSystemEventHandler(object sender, FileSystemEventArgs e)
{
_events.Add(new DelayedEvent(e));
}
private void ErrorEventHandler(object sender, ErrorEventArgs e)
{
OnError(e);
}
private void RenamedEventHandler(object sender, RenamedEventArgs e)
{
_events.Add(new DelayedEvent(e));
}
private void ElapsedEventHandler(Object sender, ElapsedEventArgs e)
{
// We don't fire the events inside the lock. We will queue them here until
// the code exits the locks.
Queue eventsToBeFired = null;
if (System.Threading.Monitor.TryEnter(_enterThread))
{
// Only one thread at a time is processing the events
try
{
eventsToBeFired = new Queue(32);
// Lock the collection while processing the events
lock (_events.SyncRoot)
{
DelayedEvent current;
for (int i = 0; i < _events.Count; i++)
{
current = _events[i] as DelayedEvent;
if (current.Delayed)
{
// This event has been delayed already so we can fire it
// We just need to remove any duplicates
for (int j = i + 1; j < _events.Count; j++)
{
if (current.IsDuplicate(_events[j]))
{
// Removing later duplicates
_events.RemoveAt(j);
j--; // Don't skip next event
}
}
bool raiseEvent = true;
if (current.Args.ChangeType == WatcherChangeTypes.Created || current.Args.ChangeType == WatcherChangeTypes.Changed)
{
//check if the file has been completely copied (can be opened for read)
FileStream stream = null;
try
{
stream = File.Open(current.Args.FullPath, FileMode.Open, FileAccess.Read, FileShare.None);
// If this succeeds, the file is finished
}
catch (IOException)
{
raiseEvent = false;
}
finally
{
if (stream != null) stream.Close();
}
}
if (raiseEvent)
{
// Add the event to the list of events to be fired
eventsToBeFired.Enqueue(current);
// Remove it from the current list
_events.RemoveAt(i);
i--; // Don't skip next event
}
}
else
{
// This event was not delayed yet, so we will delay processing
// this event for at least one timer interval
current.Delayed = true;
}
}
}
}
finally
{
System.Threading.Monitor.Exit(_enterThread);
}
}
// else - this timer event was skipped, processing will happen during the next timer event
// Now fire all the events if any events are in eventsToBeFired
RaiseEvents(eventsToBeFired);
}
public int ConsolidationInterval
{
get
{
return _consolidationInterval;
}
set
{
_consolidationInterval = value;
_serverTimer.Interval = value;
}
}
protected void RaiseEvents(Queue deQueue)
{
if ((deQueue != null) && (deQueue.Count > 0))
{
DelayedEvent de;
while (deQueue.Count > 0)
{
de = deQueue.Dequeue() as DelayedEvent;
switch (de.Args.ChangeType)
{
case WatcherChangeTypes.Changed:
OnChanged(de.Args);
break;
case WatcherChangeTypes.Created:
OnCreated(de.Args);
break;
case WatcherChangeTypes.Deleted:
OnDeleted(de.Args);
break;
case WatcherChangeTypes.Renamed:
OnRenamed(de.Args as RenamedEventArgs);
break;
}
}
}
}
#endregion
}
}
You may find the code at:
https://github.com/melenaos/FileSystemSafeWatcher/blob/master/FileSystemSafeWatcher.cs

can't get value of trackbar on menu strip

I've added a trackbar to menu strip manually because vs 2008 doesn't allow me to do.
However, i can't get the value of trackbar.
Form1.cs:
[ToolStripItemDesignerAvailability(ToolStripItemDesignerAvailability.MenuStrip |
ToolStripItemDesignerAvailability.ContextMenuStrip)]
public class TrackBarMenuItem : ToolStripControlHost
{
private TrackBar trackBar;
public TrackBarMenuItem()
: base(new TrackBar())
{
this.trackBar = this.Control as TrackBar;
trackBar.TickFrequency = 1;
trackBar.Maximum = 255;
trackBar.LargeChange = 5;
trackBar.SmallChange = 2;
}
}
Form1.Designer.cs:
private TrackBarMenuItem trackBar1;
//
// trackBar1
//
this.trackBar1.Name = "trackBar1";
this.trackBar1.Size = new System.Drawing.Size(104, 25);
and this is how i need to use it:
private void trackBar1_Scroll(object sender, System.EventArgs e)
{
int valueB = trackBar1.Value;
pictureBox2.Image = Deneme(new Bitmap(pictureBox1.Image),valueB);
}
but i get this error:
Error 1 'goruntuIsleme2.Form1.TrackBarMenuItem' does not contain a
definition for 'Value' and no extension method 'Value' accepting a
first argument of type 'goruntuIsleme2.Form1.TrackBarMenuItem' could
be found (are you missing a using directive or an assembly reference?)
any ideas?
Expose the value of the internal Trackbar object as a property on your new TrackBarMenuItem class:
Value { get { return trackBar.Value; } set { trackBar.Value = value; } }
i am adding the solution i found. someone might need it:
[System.ComponentModel.DesignerCategory("code")]
[ToolStripItemDesignerAvailability(ToolStripItemDesignerAvailability.ContextMenuStrip | ToolStripItemDesignerAvailability.MenuStrip)]
public partial class ToolStripMenuItem : ToolStripControlHost
{
public ToolStripMenuItem()
: base(CreateControlInstance())
{
}
/// <summary>
/// Create a strongly typed property called TrackBar - handy to prevent casting everywhere.
/// </summary>
public TrackBar TrackBar
{
get
{
return Control as TrackBar;
}
}
/// <summary>
/// Create the actual control, note this is static so it can be called from the
/// constructor.
///
/// </summary>
/// <returns></returns>
private static Control CreateControlInstance()
{
TrackBar t = new TrackBar();
t.AutoSize = false;
t.Height = 16;
t.Maximum = 255;
// Add other initialization code here.
return t;
}
[DefaultValue(0)]
public int Value
{
get { return TrackBar.Value; }
set { TrackBar.Value = value; }
}
/// <summary>
/// Attach to events we want to re-wrap
/// </summary>
/// <param name="control"></param>
protected override void OnSubscribeControlEvents(Control control)
{
base.OnSubscribeControlEvents(control);
TrackBar trackBar = control as TrackBar;
trackBar.ValueChanged += new EventHandler(trackBar_ValueChanged);
}
/// <summary>
/// Detach from events.
/// </summary>
/// <param name="control"></param>
protected override void OnUnsubscribeControlEvents(Control control)
{
base.OnUnsubscribeControlEvents(control);
TrackBar trackBar = control as TrackBar;
trackBar.ValueChanged -= new EventHandler(trackBar_ValueChanged);
}
/// <summary>
/// Routing for event
/// TrackBar.ValueChanged -> ToolStripTrackBar.ValueChanged
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void trackBar_ValueChanged(object sender, EventArgs e)
{
// when the trackbar value changes, fire an event.
if (this.ValueChanged != null)
{
ValueChanged(sender, e);
}
}
// add an event that is subscribable from the designer.
public event EventHandler ValueChanged;
// set other defaults that are interesting
protected override Size DefaultSize
{
get
{
return new Size(200, 16);
}
}
}
if you add this to your code, you will be able to add trackbars as ToolStripMenuItem via Designer.
Does your class TrackBarMenuItem has a property called Value? If not, you have to add it.

Problem when extending asp:Repeater with DataPager

I'm extending asp:Repeater to use DataPager, and now my code work with SqlDataSource. To get better performance, I want to use ObjectDataSource with it, but I have to use QueryStringField of DataPager, otherwise I have to click page number twice to make it work. Can any one help on this? this is my code:
namespace WebTest.UserControl
{
public class PageableRepeater : Repeater, IPageableItemContainer
{
private static readonly object EventTotalRowCountAvailable = new object();
private int _startRowIndex = 0;
private int _maximumRows = -1;
private int _totalRowCount = -1;
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
Page.RegisterRequiresControlState(this);
}
protected override void LoadControlState(object savedState)
{
_startRowIndex = 0;
_maximumRows = -1;
_totalRowCount = -1;
object[] state = savedState as object[];
if (state != null)
{
base.LoadControlState(state[0]);
if (state[1] != null)
{
_totalRowCount = (int)state[1];
}
}
else
{
base.LoadControlState(null);
}
if (!IsViewStateEnabled)
{
OnTotalRowCountAvailable(new PageEventArgs(_startRowIndex, _maximumRows, _totalRowCount));
}
}
protected override object SaveControlState()
{
object baseState = base.SaveControlState();
if (baseState != null || _totalRowCount != -1)
{
object[] state = new object[2];
state[0] = baseState;
state[1] = _totalRowCount;
return state;
}
return true;
}
protected override System.Collections.IEnumerable GetData()
{
ListViewPagedDataSource pagedDataSource = new ListViewPagedDataSource();
pagedDataSource.StartRowIndex = _startRowIndex;
pagedDataSource.MaximumRows = _maximumRows;
if (DataSource is ObjectDataSource)
{
SelectArguments.StartRowIndex = _startRowIndex;
SelectArguments.MaximumRows = _maximumRows;
SelectArguments.RetrieveTotalRowCount = true;
pagedDataSource.DataSource = base.GetData();
_totalRowCount = SelectArguments.TotalRowCount;
pagedDataSource.AllowServerPaging = true;
pagedDataSource.TotalRowCount = _totalRowCount;
}
else
{
pagedDataSource.DataSource = base.GetData();
pagedDataSource.AllowServerPaging = false;
pagedDataSource.TotalRowCount = 0;
_totalRowCount = pagedDataSource.DataSourceCount;
}
return pagedDataSource;
}
protected override void CreateControlHierarchy(bool useDataSource)
{
base.CreateControlHierarchy(useDataSource);
OnTotalRowCountAvailable(new PageEventArgs(_startRowIndex, _maximumRows, _totalRowCount));
}
private void OnTotalRowCountAvailable(PageEventArgs pageEventArgs)
{
EventHandler<PageEventArgs> handler = (EventHandler<PageEventArgs>)Events[EventTotalRowCountAvailable];
if (handler != null)
{
handler(this, pageEventArgs);
}
}
public int MaximumRows
{
get { return _maximumRows; }
}
public int StartRowIndex
{
get { return _startRowIndex; }
}
public void SetPageProperties(int startRowIndex, int maximumRows, bool databind)
{
if (maximumRows < 1)
{
throw new ArgumentOutOfRangeException("maximumRows");
}
if (startRowIndex < 0)
{
throw new ArgumentOutOfRangeException("startRowIndex");
}
_startRowIndex = startRowIndex;
_maximumRows = maximumRows;
if (databind)
{
RequiresDataBinding = true;
}
}
public event EventHandler<PageEventArgs> TotalRowCountAvailable
{
add
{
Events.AddHandler(EventTotalRowCountAvailable, value);
}
remove
{
Events.RemoveHandler(EventTotalRowCountAvailable, value);
}
}
}
}
By the way, I found another implementation to this on CodeProject, but I don't think it use IPageableItemContainer very well.
I know this is an old one, but I've recently been looking for a solution to using the Repeater with a DataPager control.
Most research takes me to this CodeProject article, but it seems like a bit of a hack to me. I've worked on this problem a bit, and put it to the side for a while as I was spinning my wheels on it, until I saw your question and sample code. I was able to take your code and apply it to mine to get a working solution (at least it works for me). With the DataPagerRepeater control below, I'm able to drop the DataPagerRepeater, DataPager, and a DataSource (ObjectDataSource, etc) on the page. Set the DataPagerRepeaters DataSource or DataSourceID to point to the data source, then set the DataPagers PagedControlID to the DataPagerRepeater. Seems to work great.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace My.Web.UI.WebControls
{
/// <summary>
/// Extends the Repeater to be compatable with a DataPager
/// </summary>
/// <remarks>
/// To page through data in a control that implements the IPageableItemContainer interface, DataPager control can be used.
/// Repeater does not support paging and does not implement IPageableItemContainer interface. ListView is the only control that works with DataPager.
///
/// The DataPager control supports built-in paging user interface (UI). NumericPagerField object enables users to select a page of data by page number.
/// NextPreviousPagerField object enables users to move through pages of data one page at a time, or to jump to the first or last page of data.
/// The size of the pages of data is set by using the PageSize property of the DataPager control. One or more pager field objects can be used in
/// a single DataPager control. Custom paging UI can be created by using the TemplatePagerField object. In the TemplatePagerField template,
/// the DataPager control is referenced by using the Container property which provides access to the properties of the DataPager control.
/// These properties include the starting row index, the page size, and the total number of rows currently bound to the control.
/// </remarks>
[ToolboxData("<my:DataPagerRepeater runat=server></my:DataPagerRepeater>")]
[Themeable(true)]
public class DataPagerRepeater : Repeater, IPageableItemContainer
{
/// <summary>Gets the maximum number of items to display on a single page of the <see cref="T:InteriorHealth.Web.UI.WebControls.DataPagerRepeater" /> control.
/// </summary>
/// <returns>The maximum number of items to display on a single page of the <see cref="T:InteriorHealth.Web.UI.WebControls.DataPagerRepeater" /> control.
/// </returns>
public int MaximumRows
{
get;
//{
// return ViewState["maxrows"] != null ? (int)ViewState["maxrows"] : -1;
//}
private set;
//{
// ViewState["maxrows"] = value;
//}
}
/// <summary>Gets the index of the first record that is displayed on a page of data in the <see cref="T:InteriorHealth.Web.UI.WebControls.DataPagerRepeater" /> control.
/// </summary>
/// <returns>The index of the first record that is displayed on a page of data in the <see cref="T:InteriorHealth.Web.UI.WebControls.DataPagerRepeater" /> control.
/// </returns>
public int StartRowIndex
{
get;
//{
// return ViewState["startrowindex"] != null ? (int)ViewState["startrowindex"] : -1;
//}
private set;
//{
// ViewState["startrowindex"] = value;
//}
}
/// <summary>
/// Total number of rows that are avialable, regardless of what is currently being displayed
/// </summary>
private int TotalRowsAvailable
{
get;
//{
// return ViewState["totalrows"] != null ? (int)ViewState["totalrows"] : -1;
//}
set;
//{
// ViewState["totalrows"] = value;
//}
}
private static readonly object EventPagePropertiesChanged = new object();
/// <summary>Occurs when the page properties change, after the <see cref="T:InteriorHealth.Web.UI.WebControls.DataPagerRepeater" /> control sets the new values.
/// </summary>
public event EventHandler PagePropertiesChanged
{
add
{
base.Events.AddHandler(DataPagerRepeater.EventPagePropertiesChanged, value);
}
remove
{
base.Events.RemoveHandler(DataPagerRepeater.EventPagePropertiesChanged, value);
}
}
private static readonly object EventPagePropertiesChanging = new object();
/// <summary>Occurs when the page properties change, but before the <see cref="T:InteriorHealth.Web.UI.WebControls.DataPagerRepeater" /> control sets the new values.
/// </summary>
public event EventHandler<PagePropertiesChangingEventArgs> PagePropertiesChanging
{
add
{
base.Events.AddHandler(DataPagerRepeater.EventPagePropertiesChanging, value);
}
remove
{
base.Events.RemoveHandler(DataPagerRepeater.EventPagePropertiesChanging, value);
}
}
private static readonly object EventTotalRowCountAvailable = new object();
/// <summary>For a description of this member, see <see cref="E:System.Web.UI.WebControls.IPageableItemContainer.TotalRowCountAvailable" />.
/// </summary>
public event EventHandler<PageEventArgs> TotalRowCountAvailable
{
add
{
base.Events.AddHandler(DataPagerRepeater.EventTotalRowCountAvailable, value);
}
remove
{
base.Events.RemoveHandler(DataPagerRepeater.EventTotalRowCountAvailable, value);
}
}
/// <summary>
/// Register the control as one who's control state needs to be persisted
/// </summary>
/// <param name="e"></param>
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
Page.RegisterRequiresControlState(this);
}
/// <summary>
/// Initialize the start row index, maximum rows, and total rows available values. Load the total rows available from viewstate.
/// </summary>
/// <param name="savedState"></param>
protected override void LoadControlState(object savedState)
{
this.StartRowIndex = 0;
this.MaximumRows = -1;
this.TotalRowsAvailable = -1;
object[] state = savedState as object[];
if (state != null)
{
base.LoadControlState(state[0]);
if (state[1] != null)
{
this.TotalRowsAvailable = (int)state[1];
}
}
else
{
base.LoadControlState(null);
}
if (!IsViewStateEnabled)
{
OnTotalRowCountAvailable(new PageEventArgs(this.StartRowIndex, this.MaximumRows, this.TotalRowsAvailable));
}
}
/// <summary>
/// Save the total rows available value to viewstate
/// </summary>
/// <returns></returns>
protected override object SaveControlState()
{
object baseState = base.SaveControlState();
if (baseState != null || this.TotalRowsAvailable != -1)
{
object[] state = new object[2];
state[0] = baseState;
state[1] = this.TotalRowsAvailable;
return state;
}
return true;
}
/// <summary>Sets the properties of a page of data in the <see cref="T:InteriorHealth.Web.UI.WebControls.DataPagerRepeater" /> control.
/// </summary>
/// <param name="startRowIndex">The index of the first record on the page.</param>
/// <param name="maximumRows">The maximum number of items on a single page.</param>
/// <param name="databind">true to rebind the control after the properties are set; otherwise, false.</param>
/// <exception cref="T:System.ArgumentOutOfRangeException">
/// <paramref name="maximumRows" /> is less than 1.-or-<paramref name="startRowIndex" /> is less than 0.
/// </exception>
public void SetPageProperties(int startRowIndex, int maximumRows, bool databind)
{
if (maximumRows < 1)
{
throw new ArgumentOutOfRangeException("maximumRows");
}
if (startRowIndex < 0)
{
throw new ArgumentOutOfRangeException("startRowIndex");
}
if (this.StartRowIndex != startRowIndex || this.StartRowIndex != maximumRows)
{
PagePropertiesChangingEventArgs pagePropertiesChangingEventArgs = new PagePropertiesChangingEventArgs(startRowIndex, maximumRows);
if (databind)
{
this.OnPagePropertiesChanging(pagePropertiesChangingEventArgs);
}
this.StartRowIndex = pagePropertiesChangingEventArgs.StartRowIndex;
this.MaximumRows = pagePropertiesChangingEventArgs.MaximumRows;
if (databind)
{
this.OnPagePropertiesChanged(EventArgs.Empty);
}
}
if (databind)
{
this.RequiresDataBinding = true;
}
//this.OnTotalRowCountAvailable(new PageEventArgs(this.StartRowIndex, this.MaximumRows, this.TotalRowsAvailable));
}
/// <summary>
/// Creates a control hierarchy, with or without the specified data source.
/// </summary>
/// <param name="useDataSource">
/// Indicates whether to use the specified data source.
/// </param>
protected override void CreateControlHierarchy(bool useDataSource)
{
base.CreateControlHierarchy(useDataSource);
OnTotalRowCountAvailable(new PageEventArgs(this.StartRowIndex, this.MaximumRows, this.TotalRowsAvailable));
}
/// <summary>Returns an <see cref="T:System.Collections.IEnumerable" /> interface from the data source.</summary>
/// <returns>An object implementing <see cref="T:System.Collections.IEnumerable" /> that represents the data from the data source.</returns>
protected override System.Collections.IEnumerable GetData()
{
System.Collections.IEnumerable data = base.GetData();
this.TotalRowsAvailable = this.SelectArguments.TotalRowCount;
this.OnTotalRowCountAvailable(new PageEventArgs(this.StartRowIndex, this.MaximumRows, this.TotalRowsAvailable));
return data;
}
/// <summary>Raises the <see cref="E:InteriorHealth.Web.UI.WebControls.DataPagerRepeater.PagePropertiesChanging" /> event.
/// </summary>
/// <param name="e">The event data.</param>
protected virtual void OnPagePropertiesChanging(PagePropertiesChangingEventArgs e)
{
EventHandler<PagePropertiesChangingEventArgs> eventHandler = (EventHandler<PagePropertiesChangingEventArgs>)base.Events[DataPagerRepeater.EventPagePropertiesChanging];
if (eventHandler != null)
{
eventHandler(this, e);
}
}
/// <summary>Raises the <see cref="E:InteriorHealth.Web.UI.WebControls.DataPagerRepeater.SelectedIndexChanged" /> event.</summary>
/// <param name="e">The event data.</param>
protected virtual void OnPagePropertiesChanged(EventArgs e)
{
EventHandler eventHandler = (EventHandler)base.Events[DataPagerRepeater.EventPagePropertiesChanged];
if (eventHandler != null)
{
eventHandler(this, e);
}
}
/// <summary>Raises the <see cref="E:InteriorHealth.Web.UI.WebControls.DataPagerRepeater.PagePropertiesChanging" /> event.</summary>
/// <param name="e">The event data.</param>
protected virtual void OnTotalRowCountAvailable(PageEventArgs e)
{
EventHandler<PageEventArgs> eventHandler = (EventHandler<PageEventArgs>)base.Events[DataPagerRepeater.EventTotalRowCountAvailable];
if (eventHandler != null)
{
eventHandler(this, e);
}
}
/// <summary>
/// Override the selection of rows that we need
/// </summary>
/// <returns></returns>
protected override DataSourceSelectArguments CreateDataSourceSelectArguments()
{
DataSourceSelectArguments arg = base.CreateDataSourceSelectArguments();
arg.StartRowIndex = this.StartRowIndex;
arg.MaximumRows = this.MaximumRows;
return arg;
}
}
}

Categories

Resources