I am using ListViewCollection class with my dataGrid. The underlying collection is an observable collection.
Whenever i call Move methods in the collection ( which is in ViewModel), the CurrentChanged Event doesnt fire.
However when UI calls the same method on it ( i can see it in the call stack), the event does fire.
this.EmailTemplates = new ListCollectionView(templateVmList);
this.EmailTemplates.CurrentChanging += (o, e) => EmailTemplates_CurrentChanging(o, e);
this.EmailTemplates.CurrentChanged += (o, e) => { this.SelectedEmailTemplate = (EmailTemplateViewModel)this.EmailTemplates.CurrentItem; };
if (this.EmailTemplates.Count > 0)
{
if (!this.EmailTemplates.MoveCurrentToFirst())
throw new ArgumentException("Element not found in collection");
}
What should i do in code to make sure the events fire no matter who is changing the collection.
Try using CollectionViewSource.GetDefaultView instead of creating a new ListCollectionView.
This test code worked fine for me
public class LcViewModel : BaseItemsViewModel
{
public LcViewModel()
{
MoveCommand = new RelayCommand(Move);
var view = CollectionViewSource.GetDefaultView(Items);
view.CurrentChanged += (sender, args) => Debug.WriteLine("CurrentChanged");
view.CurrentChanging += (sender, args) => Debug.WriteLine("CurrentChanging");
}
public ICommand MoveCommand { get; private set; }
private void Move()
{
var view = CollectionViewSource.GetDefaultView(Items);
view.MoveCurrentToFirst();
}
}
Related
I'm trying to subscribe to the Activate event of an NSStatusBarButton object in AppDelegate's DidFinishLaunching() but the event never gets invoked.
The purpose is to get notified when the top menu bar icon of the application is clicked so its contents can get populated dynamically.
using AppKit;
using Foundation;
[Register("AppDelegate")]
public class AppDelegate : NSApplicationDelegate
{
private NSStatusItem _statusBar;
public override void DidFinishLaunching(NSNotification notification)
{
this._statusBar = NSStatusBar.SystemStatusBar.CreateStatusItem(NSStatusItemLength.Variable);
this._statusBar.Title = "MyApp";
this._statusBar.HighlightMode = true;
this._statusBar.Menu = new NSMenu("MyApp");
// Working example on NSMenuItem object
var someItem = new NSMenuItem("Some Item");
someItem.Activated += (sender, e) =>
{
System.Diagnostics.Debug.WriteLine("This one does fire.");
};
this._statusBar.Menu.AddItem(someItem);
// Problem
this._statusBar.Button.Activated += (sender, e) =>
{
System.Diagnostics.Debug.WriteLine("This one does not fire.");
};
}
}
It does not fire because you attached a menu. The button action is popping up the menu and the activated event of the button is never fired. If you remove the menu the button event will run.
Either remove the menu and use it as a button. Then your event will fire. Or just use the menu.
If you want to run custom code when the menu is shows set a delegate of the NSMenu:
using AppKit;
using Foundation;
public class MyMenuDelegate : NSObject, INSMenuDelegate
{
public void MenuWillHighlightItem(NSMenu menu, NSMenuItem item)
{
}
[Export("menuWillOpen:")]
public void MenuWillOpen(NSMenu menu)
{
// your code here
}
}
[Register("AppDelegate")]
public class AppDelegate : NSApplicationDelegate
{
private NSStatusItem _statusBar;
MyMenuDelegate _menuDel;
public override void DidFinishLaunching(NSNotification notification)
{
_statusBar = NSStatusBar.SystemStatusBar.CreateStatusItem(NSStatusItemLength.Variable);
_statusBar.Title = "MyApp";
_statusBar.HighlightMode = true;
_statusBar.Menu = new NSMenu("MyApp");
_menuDel = new MyMenuDelegate();
_statusBar.Menu.Delegate = _menuDel;
// Working example on NSMenuItem object
var someItem = new NSMenuItem("Some Item");
someItem.Activated += (sender, e) =>
{
System.Diagnostics.Debug.WriteLine("This one does fire.");
};
_statusBar.Menu.AddItem(someItem);
}
}
In my app, I want to know in the View when Initialize is complete. The problem is that it gets launched before you can hook InitializeTask.PropertyChanged. Here is my ViewModel code:
public override async Task Initialize()
{
ClientID = await MyDataSource.GetClientID();
}
In my View I am doing the following:
protected override void OnViewModelSet()
{
var vm = this.DataContext as MyViewModel;
vm.InitializeTask.PropertyChanged += InitializeTask_PropertyChanged;
base.OnViewModelSet();
}
private void InitializeTask_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == "IsSuccessfullyCompleted")
{
vm.InitializeTask.PropertyChanged -= InitializeTask_PropertyChanged;
if (vm.ClientID != "")
posClient = new PosClient(this, vm.ClientID);
}
}
As far as I know, OnViewModelSet is the soonest that you have access to your ViewModel in your View. If I put in breakpoints, Initialize runs before OnViewModelSet is fired. This makes it very likely that Initialize has finished before you can hook the event. Is there another place in the View where you can hook the event before Initialize starts so you will be guaranteed to Initialize.PropertyChanged will fire in the View?
**** Update ****
I followed #fmaccaroni advice and implemented an MvxInteraction called DataLoaded. One thing I did different was to create a separate function to load the data.
public void LoadData()
{
Task.Run(async () =>
{
ClientID = await IHSDataSource.GetClientID();
_DataLoaded.Raise();
});
}
I was concerned about the async task finishing before I got the interaction event wired up. Doing it this way, I added this in the View.
protected override void OnViewModelSet()
{
vm = this.DataContext as InvoiceViewModel;
var set = this.CreateBindingSet<InvoiceView, InvoiceViewModel>();
set.Bind(this).For(view => view.DataLoaded).To(viewModel => viewModel.DataLoaded).OneWay();
set.Apply();
vm.LoadData();
base.OnViewModelSet();
}
This way, LoadData does not start until I am sure the result will trigger the interaction and I am guaranteed to get the result. This was the first I had heard about MvxInteraction and I am now using it all the time.
I'm not sure what you want to achieve, but if you want to take an action in your view when the Initialize ends just do an MvxInteraction and call it after your await, i.e.:
ViewModel:
private MvxInteraction _interaction = new MvxInteraction();
public IMvxInteraction MyMvxInteraction => _interaction;
public override async Task Initialize()
{
ClientID = await MyDataSource.GetClientID();
this._interaction.Raise();
}
View:
private IMvxInteraction _interaction;
public IMvxInteraction MyMvxInteraction
{
get => this._interaction;
set
{
if (this._interaction != null)
this._interaction.Requested -= this.OnInteractionRequested;
this._interaction = value;
this._interaction.Requested += this.OnInteractionRequested;
}
}
private void OnInteractionRequested(object sender, EventArgs e)
{
var vm = this.DataContext as MyViewModel;
if (vm.ClientID != "")
posClient = new PosClient(this, vm.ClientID);
}
and the binding in the view:
var set = this.CreateBindingSet<MyView, MyViewModel>();
set.Bind(this).For(view => view.MyMvxInteraction).To(viewModel => viewModel.MyMvxInteraction).OneWay();
set.Apply();
I have a custom UserControl called ClosableTabItem which inherits from the TabItem control. I simply added a save button and a close button and I'm trying to wire in some event handlers. When the user clicks on the X (close), I want to invoke a "OnClosing" event with cancellation event arguments so that the user can put in some logic in the OnClosing event and if needed, cancel the close operation just like you can on a windows FormClosing event.
I'm not sure how I can fire the event and wait for a response before removing the tabitem from the collection.
Any ideas?
Thanks.
public class ClosableButtonTabItem : TabItem
{
private readonly cTabButtonHeader _closableTabHeader;
public event EventHandler<TabButtonClickEventArgs> OnTabButtonClick;
public event EventHandler<System.ComponentModel.CancelEventArgs> OnTabClosing;
public event EventHandler OnTabClosed;
public UserControl AttachedForm { get; set; }
public string Title
{
get => ((cTabButtonHeader) this.Header).label_TabTitle.Content.ToString();
set => ((cTabButtonHeader)this.Header).label_TabTitle.Content = value;
}
public ClosableButtonTabItem()
{
_closableTabHeader = new cTabButtonHeader();
Header = _closableTabHeader;
_closableTabHeader.button_close.Source =
ImageHelper.LocalPathToImageSource(ImageHelper.ImageSizes.Size_32x32, "x_off.png");
_closableTabHeader.button_close.MouseEnter += button_close_MouseEnter;
_closableTabHeader.button_close.MouseLeave += button_close_MouseLeave;
_closableTabHeader.button_close.MouseLeftButtonDown += button_close_MouseLeftButtonDown;
_closableTabHeader.label_TabTitle.SizeChanged += label_TabTitle_SizeChanged;
//closableTabHeader.button_group.MouseEnter += button_save_MouseEnter;
//closableTabHeader.button_group.MouseLeave += button_save_MouseLeave;
_closableTabHeader.button_save.MouseLeftButtonDown += button_save_MouseLeftButtonDown;
}
void button_close_MouseLeftButtonDown(object sender, MouseButtonEventArgs mouseButtonEventArgs)
{
OnTabClosing?.Invoke(this, new CancelEventArgs());
//Code somewhere that if they don't cancel the OnClosing event the run:
((TabControl)Parent).Items.Remove(this); }
}
I found it.
void button_close_MouseLeftButtonDown(object sender, MouseButtonEventArgs mouseButtonEventArgs)
{
if (OnTabClosing == null)
{
((TabControl)Parent).Items.Remove(this);
OnTabClosed?.Invoke(this, EventArgs.Empty);
return;
}
foreach (var subHandler in OnTabClosing.GetInvocationList())
{
var cea = new TabButtonClosingEventArgs(AttachedForm);
OnTabClosing?.Invoke(this, cea);
if (cea.Cancel) continue;
((TabControl)Parent).Items.Remove(this);
OnTabClosed?.Invoke(this, EventArgs.Empty);
}
}
ObservableCollection<> exposes the CollectionChanged event. From the handler of that event is there any way to get a reference to the ObservableCollection that fired the event? I would have thought the sender argument would be the ObservableCollection but it's not.
This code sample illustrates that 10 ObservableCollections all have their CollectionChanged event registered to one method. From that one method I'd like to get the reference to the ObservableCollection that changed:
internal class Program
{
private static void Main(string[] args)
{
List<ObservableCollection<int>> collections = new List<ObservableCollection<int>>();
for (int i = 0; i < 10; i++)
{
ObservableCollection<int> collection = new ObservableCollection<int>();
collection.CollectionChanged += CollectionOnCollectionChanged;
collections.Add(collection);
}
collections[5].Add(1234);
}
private static void CollectionOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs)
{
if (notifyCollectionChangedEventArgs.Action == NotifyCollectionChangedAction.Add)
{
// Before proceeding I need to get a reference to the ObservableCollection<int> where the change occured which fired this event.
}
}
}
Looking at the arguments passed into the event handler, I don't see a reference to the ObservableCollection so I'm assuming I can't get it.
The sender object is the instance of the ObservableCollection that fired the event. You can cast it.
private static void CollectionOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs)
{
if (notifyCollectionChangedEventArgs.Action == NotifyCollectionChangedAction.Add)
{
ObservableCollection<int> myCollection = sender as ObservableCollection<int>;
if(myCollection != null){
//do whatever you want
}
}
}
One other option you have is to use an in-line delegate and form a closure over the collection like this:
private static void Main(string[] args)
{
List<ObservableCollection<int>> collections = new List<ObservableCollection<int>>();
for (int i = 0; i < 10; i++)
{
ObservableCollection<int> collection = new ObservableCollection<int>();
collection.CollectionChanged += (s, e) =>
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
// Can reference `collection` directly here
}
};
collections.Add(collection);
}
collections[5].Add(1234);
}
The saves casting from the sender object (which I find messy.)
A further way to write this could would be:
private static void Main(string[] args)
{
List<ObservableCollection<int>> collections =
Enumerable
.Range(0, 10)
.Select(n => new ObservableCollection<int>())
.ToList();
collections
.ForEach(collection =>
{
collection.CollectionChanged += (s, e) =>
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
// Can reference `collection` directly here
}
};
});
collections[5].Add(1234);
}
The Dart programming language has support for method cascades. Method cascades would allow the following Silverlight/WPF C# code:
var listBox = new ListBox();
listBox.Width = 200;
listBox.MouseEnter += (s, e) => Console.WriteLine("MouseEnter");
var button1 = new Button() { Content = "abc" };
button1.Click += (s, e) => Console.WriteLine("button1.Click");
listBox.Items.Add(button1);
var button2 = new Button() { Content = "def" };
button2.Click += (s, e) => Console.WriteLine("button2.Click");
listBox.Items.Add(button2);
ContentPanel.Children.Add(listBox);
to be written instead as:
ContentPanel.Children.Add(
new ListBox()
..Width = 200
..MouseEnter += ((s, e) => Console.WriteLine("MouseEnter"))
..Items.Add(
new Button()
..Content = "abc";
..Click += ((s, e) => Console.WriteLine("button 1 Click")))
..Items.Add(
new Button()
..Content = "def";
..Click += (s, e) => (Console.WriteLine("button 2 Click"))));
My question is, is there a way to simulate or closely approximate method cascades in C#?
Here's one approach I came up with. Given this extension method:
public static T Call<T>(this T obj, Action<T> proc)
{
proc(obj);
return obj;
}
the above example can be written as follows:
ContentPanel.Children.Add(
new ListBox().Call(o => {
o.Width = 200;
o.MouseEnter += (s, e) => Console.WriteLine("MouseEnter");
o.Items.Add(
new Button().Call(b => {
b.Content = "abc";
b.Click += (s, e) => Console.WriteLine("button 1 Click"); }));
o.Items.Add(
new Button().Call(b => {
b.Content = "def";
b.Click += (s, e) => Console.WriteLine("button 2 Click"); })); }));
I wouldn't argue that that's pretty. :-) But it does essentially enable a fluent style to be applied.
I think you can reach close to what you want to achieve by using fluent interface. It will allow you to chain methods to create and initialize objects in one statement.
You can get something like that:
Fluent fluent = new Fluent();
var panel = fluent.CreateControlPanel().Children()
.AddListBox().SetWidth(200).AddMouseEnterEvent((s, e) => { }).Create()
.AddTextBox().SetText("Foo").Create()
.GetControlPanel();
The idea is that a method returns an object allowing to initialize another object. A chain of initializer can call at any item a "finalizer" method (above Create) that returns the original object (above Children) to continue to add other objects or configure the initial one.
So for example in AddListBox returns an object of type ListBoxSetup which has a bunch of methods like SetWidth or AddMouseEnterEvent. In this case Children will also be a special object (like of type ChildSetup) which has a bunch of methods such as AddListBox or AddTextBox. Each of the method has in charge to create an object of type ListBox or TextBox or setup properties of the underlying object to be created. Fluent will have a method that returns your whole object structure correctly setup.
Take a look to this link:
http://blog.raffaeu.com/archive/2010/06/26/how-to-write-fluent-interface-with-c-and-lambda.aspx
Here's an example of the underlying code create to end up with the above. Of course the code could be greatly improved in its architecture but it's here just for the sake of the example.
public class Fluent
{
public ControlPanelCreator CreateControlPanel()
{
return new ControlPanelCreator(new StackPanel(), this);
}
}
public class ControlPanelCreator
{
#region Fields
private Fluent fluent;
private Panel panel;
#endregion
#region Constructors
internal ControlPanelCreator(Panel panel, Fluent fluent)
{
this.fluent = fluent;
this.panel = panel;
}
#endregion
#region Methods
public ControlPanelChildrenCreator Children()
{
return new ControlPanelChildrenCreator(this.panel, this);
}
#endregion
}
public class ControlPanelChildrenCreator
{
#region Fields
private ControlPanelCreator panelCreator;
private Panel panel;
#endregion
#region Constructors
internal ControlPanelChildrenCreator(Panel panel, ControlPanelCreator panelCreator)
{
this.panel = panel;
this.panelCreator = panelCreator;
}
#endregion
#region Methods
public ListBoxCreator AddListBox()
{
ListBox listBox = new ListBox();
this.panel.Children.Add(listBox);
return new ListBoxCreator(listBox, this);
}
public TextBoxCreator AddTextBox()
{
TextBox textBox = new TextBox();
this.panel.Children.Add(textBox);
return new TextBoxCreator(textBox, this);
}
public Panel GetControlPanel()
{
return this.panel;
}
#endregion
}
public class ListBoxCreator
{
#region Fields
private ListBox listbox;
private ControlPanelChildrenCreator parentCreator;
#endregion
#region Constructors
internal ListBoxCreator(ListBox listBox, ControlPanelChildrenCreator parentCreator)
{
this.listbox = listBox;
this.parentCreator = parentCreator;
}
#endregion
#region Methods
public ListBoxCreator SetWidth(int width)
{
this.listbox.Width = width;
return this;
}
public ListBoxCreator AddMouseEnterEvent(Action<object, MouseEventArgs> action)
{
this.listbox.MouseEnter += new MouseEventHandler(action);
return this;
}
public ControlPanelChildrenCreator Create()
{
return this.parentCreator;
}
#endregion
}
public class TextBoxCreator
{
#region Fields
private TextBox textBox;
private ControlPanelChildrenCreator parentCreator;
#endregion
#region Constructors
internal TextBoxCreator(TextBox textBox, ControlPanelChildrenCreator parentCreator)
{
this.textBox = textBox;
this.parentCreator = parentCreator;
}
#endregion
#region Methods
public TextBoxCreator SetText(string defaultText)
{
this.textBox.Text = defaultText;
return this;
}
public ControlPanelChildrenCreator Create()
{
return this.parentCreator;
}
#endregion
}
I supported previous answer. Few things I like to add, as I have created similar type of things.
There are two things, either you are having single class doing things. Means Form, has addcolor, addData etc and may be form has button and than button has color
Now in this case you need to chain using interface, means method return type will be interface, and all interface is implemented by that class and that methods return "this" only.
This will do the trick, while you create object of interface. And then chain it across. It will be difficult to give example here, but if you still want I can provide example.
Let me know if any further details required