Custom Events from User Control to Parent Control in WPF - c#

I have a simple TabControl inside my UserControl which is embedded in the parent control like this:
<StackPanel>
<myviews:myusercontrol></myviews:myusercontrol>
</StackPanel>
Inside myusercontrol I have the following implementation:
public partial class MyUserControl
{
public delegate void SelectedTabItemHandler(string selectedTabItem);
public event SelectedTabItemHandler TabItemSelected;
public MyUserControl()
{
InitializeComponent();
}
private void TabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if(TabItemSelected != null)
{
TabItemSelected("some selected Item");
}
}
}
How can I hook up the TabItemSelected event in my XAML like this:
<myviews:myusercontrol TabItemSelected="TabSelectionChanged"></myviews:myusercontrol>
UPDATE 2:
For passing custom parameters I am doing something like this:
public partial class PatientEditTabView : UserControl
{
// Routed Event
public static readonly RoutedEvent SelectedTabItemEvent = EventManager.RegisterRoutedEvent("TabItemSelected", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(PatientEditTabView));
public PatientEditTabView()
{
InitializeComponent();
}
public event RoutedEventHandler TabItemSelected
{
add { AddHandler(SelectedTabItemEvent, value); }
remove { RemoveHandler(SelectedTabItemEvent, value); }
}
private void TabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
SelectedTabRoutedEventArgs eventArgs = new SelectedTabRoutedEventArgs(PatientEditTabView.SelectedTabItemEvent,tabControl.SelectedValue as String);
RaiseEvent(eventArgs);
}
}
UPDATE 3:
public class SelectedTabRoutedEventArgs : RoutedEventArgs
{
private readonly String _selectedItem;
public SelectedTabRoutedEventArgs(RoutedEvent routedEvent,string selectedItem)
: base(routedEvent)
{
_selectedItem = selectedItem;
}
public string SelectedItem
{
get { return _selectedItem; }
}
}

You need to create custom Routed event in case you want to hook handler from XAML.
Refer to the article here - How to create custom routed event.
public partial class MyUserControl
{
public static readonly RoutedEvent TabItemSelectedEvent =
EventManager.RegisterRoutedEvent("TabItemSelected",
RoutingStrategy.Bubble, typeof(RoutedEventHandler),
typeof(MyUserControl));
public event RoutedEventHandler TabItemSelected
{
add { AddHandler(TabItemSelectedEvent, value); }
remove { RemoveHandler(TabItemSelectedEvent, value); }
}
void RaiseTabItemSelectedEvent()
{
RoutedEventArgs newEventArgs =
new RoutedEventArgs(MyUserControl.TabItemSelectedEvent);
RaiseEvent(newEventArgs);
}
private void TabControl_SelectionChanged(object sender,
SelectionChangedEventArgs e)
{
RaiseTabItemSelectedEvent();
}
}
Note: You need to create class deriving from RoutedEventArgs in case you want to pass some custom data like string in your case and pass your custom event args while raising an event.
UPDATE:
Like I said you have to create derived class from RoutedEventArgs in case want to pass string value. This is how it will go:
public class SelectedTabRoutedEventArgs : RoutedEventArgs
{
private readonly string selectedItem;
public SelectedTabRoutedEventArgs(RoutedEvent routedEvent,
string selectedItem)
:base(routedEvent)
{
this.selectedItem = selectedItem;
}
public string SelectedItem
{
get
{
return selectedItem;
}
}
}
and update other methods like this:
void RaiseTabItemSelectedEvent(string selectedItem)
{
SelectedTabRoutedEventArgs newEventArgs =
new SelectedTabRoutedEventArgs(SampleUserControl.TabItemSelectedEvent,
selectedItem);
RaiseEvent(newEventArgs);
}
private void TabControl_SelectionChanged(object sender,
SelectionChangedEventArgs e)
{
RaiseTabItemSelectedEvent(tabControl.SelectedValue.ToString());
}
Also you target event handler in window will look like this:
private void TabSelectionChanged(object sender, RoutedEventArgs e)
{
SelectedTabRoutedEventArgs args = (SelectedTabRoutedEventArgs)e;
string selectedItem = args.SelectedItem;
}

Related

Subdialog is shown one more time on each activation of parent Dialog

I developed an app, where the user sects on order from a list. If the order is selected, the EditOrderWindow shows. From this window, the user can add items to the Order by. This is done in a sub dialog AddItemWindow. Works fine for the first order.
However, if a second order is edited, the AddItemWindow shows again after its been closed. For the third order edited it shows three times aso.
I couldn't figure out why this happens, and how to prevent it.
ViewModel
public class ViewModel
{
public ICommand EditOrderCommand { get; set; }
public ICommand AddItemCommand { get; set; }
public event EventHandler<int> EditOrder;
public event EventHandler<int> AddItem;
public event EventHandler<int> ItemAdded;
public ViewModel()
{
//Is fired when an item in the datagrid in Control OrderList is doubleclicked
EditOrderCommand = new ICommand();
EditOrderCommand.Executed += EditOrderCommand_Executed();
//Is fired when the users click on add in EditOrderWindow
AddItemCommand = new ICommand();
AddItemCommand.Executed += AddItemCommand_Executed();
}
private void EditOrderCommand_Executed(object sender, EventArgs e)
{
if (SelectedOrder != null)
{
EditOrder(this, SelectedOrder.Id);
}
}
private void AddItemCommand_Executed(object sender, EventArgs e)
{
AddItem(this, new EventArgs());
}
}
Control: OrderList
public partial class OrderList : UserControl
{
ViewModel vm;
public OrderList()
{
InitializeComponent();
//DataContext is set in xmal
vm = (ViewModel)this.DataContext;
vm.EditOrder += Vm_EditOrder;
}
private void Vm_EditOrder(object sender, int e)
{
EditOrderWindow s = new EditOrderWindow(vm);
s.ShowDialog();
s = null;
}
Window: EditOrderWindow
public partial class EditOrderWindow : Window
{
ViewModel vm;
AddItemWindow aiw;
public EditOrderWindow(ViewModel _vm)
{
InitializeComponent();
vm = _vm;
this.DataContext = vm;
vm.AddItem -= Vm_AddItem;
vm.AddItem += Vm_AddItem;
}
private void Vm_AddItem(object sender, EventArgs e)
{
if (aiw == null)
{
aiw = new AddItemWindow(vm);
}
aiw.ShowDialog();
aiw = null;
}
}
Window: AddItemWindow
public partial class AddItemWindow : Window
{
public AddItemWindow(ViewModel _vm)
{
InitializeComponent();
this.DataContext = _vm;
_vm.ItemAdded -= _vm_ItemAdded;
_vm.ItemAdded += _vm_ItemAdded;
}
private void _vm_ItemAdded(object sender, EventArgs e)
{
this.Close();
}
}

c# visual studio custom control binding issue

I would like to create a simple custom control (actually a type of button). I have created the control but the step I am missing is how to add the binding. The control code I have looks like this:
public partial class PopUpButton : UserControl
{
public PopUpButton()
{
InitializeComponent();
_checked = false;
DrawButton();
}
public delegate void ChangedEventHandler(object sender, EventArgs e);
public event ChangedEventHandler OnValueChanged;
private bool _checked;
private String _text;
private void UserControl1_Resize(object sender, EventArgs e)
{
DrawButton();
}
[Bindable(true)]
public bool Checked
{
get
{
return _checked;
}
set
{
_checked = value;
DrawButton();
if (OnValueChanged != null)
{
OnValueChanged.Invoke(this, new EventArgs());
}
}
}
[Bindable(true)]
public String DisplayText
{
get
{
return _text;
}
set
{
_text = value;
DrawButton();
}
}
private void DrawButton()
{
// do some stuff
}
private void PopUpButton_Click(object sender, EventArgs e)
{
_checked = !_checked;
DrawButton();
if (OnValueChanged != null)
{
OnValueChanged.Invoke(this, new EventArgs());
}
}
}
The call to bind to the control looks like this:
regControl1.DataBindings.Clear();
regControl1.DataBindings.Add("Checked", CustomButton1, "Checked");
I know that I need to define a data source and member but cannot see how to implement this. When the above binding is called then regControl1 updates with the value of "Checked" however the function "OnValueChanged" is always null so the binding has failed, thus when "Checked" changes "regControl1" is not updated.
Ideas anyone?
Finally got something working. This solution handles a click both on the body and on the label and re-sizes the component during design. I was almost there but hope this helps:
public partial class PopUpButton : UserControl, INotifyPropertyChanged
{
public PopUpButton()
{
InitializeComponent();
_checked = false;
DrawButton();
}
private bool _checked;
private String _text;
public event PropertyChangedEventHandler PropertyChanged;
public event EventHandler ButtonClick;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
protected void Button_Click(object sender, EventArgs e)
{
_checked = !_checked;
DrawButton();
NotifyPropertyChanged("Checked");
if (this.ButtonClick != null)
this.ButtonClick(this, e);
}
private void UserControl1_Resize(object sender, EventArgs e)
{
DrawButton();
}
[Bindable(true)]
public bool Checked
{
get
{
return _checked;
}
set
{
_checked = value;
DrawButton();
NotifyPropertyChanged("Checked");
}
}
[Bindable(true)]
public String DisplayText
{
get
{
return _text;
}
set
{
_text = value;
DrawButton();
}
}
private void HandleClick( )
{
_checked = !_checked;
DrawButton();
NotifyPropertyChanged("Checked");
}
private void DrawButton()
{
//do some drawing stuff here
}
private void PopUpButton_Resize(object sender, EventArgs e)
{
DrawButton();
}
}

WPF - Model method as Execute event for a command (NO RelayCommand or DelegateCommand)

I want to use a method which is not in the code behind for the command's Executed event and another for the CanExecute.
I'm using a RoutedCommand and i do NOT want to use a Delegate Command or a Relay Command.
What I got is a Commands class:
public class Commands
{
static Commands()
{
syncCommand = new RoutedUICommand("Sync", "syncCommand", typeof(Commands));
undoCommand = new RoutedUICommand("Undo", "UndoCommand", typeof(Commands));
}
private static MyHandler handler;
public static MyHandler Handler
{
set { handler = value; }
get { return handler; }
}
private static RoutedUICommand syncCommand;
public static RoutedUICommand SyncCommand
{
get
{ return syncCommand; }
}
private static void SyncCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = Handler != null;
}
private static void SyncExecute(object sender, ExecutedRoutedEventArgs e)
{
Handler.Action();
}
private static RoutedUICommand undoCommand;
public static RoutedUICommand UndoCommand
{
get { return undoCommand; }
}
private static void UndoCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = Handler.WereChangesMade();
}
private static void UndoExecute(object sender, ExecutedRoutedEventArgs e)
{
Handler.Undo();
}
}
And in the ViewModel I got the ICommand properties and buttons with the Command property that binds to these properties.
I want to use the above methods as the methods I'll pass to the Command Binding. How do I do it when they're not in the code behind?
You need to add a CommandBinding to one of your UI elements, as the RoutedUICommand needs to tunnel and bubble somewhere to be handled. If all else fails, your MainWindow.
CommandBindings.Add(new CommandBinding(Commands.SyncCommand, Commands.SyncExecute, Commands.SyncCanExecute));
SyncExecute and SyncCanExecute need to be accessible to where this binding is made, so private won't do.
Hi i wrote a "sort of" custom CommandBinding, where you can bind your RoutedCommand to a ICommand. (How to bind a RelayCommand(MVVM) to a RoutedCommand? (CommandBinding))

Can I prevent ListBox.RefreshItem(object item) from removing and re-adding the object?

The issue I am having is that when I update my object, the ListBox automatically removes and then re-adds the object to the list, thus calling the index and value changed events. I was able to prevent this by creating a custom ListBox control and when the PropertyChangedEvent was called, I would raise a flag that would prevent those events in the base class from being called. What is happening now is that my entire reference is being replace by a new reference and unless I re-select the item in the ListBox, I have the wrong reference.
What I basically want to do, is to change the Display Value in my object and then have it update only the text in the list box. I do not want it to remove and to re-add the object/reference/whatever it does. It's quite annoying.
Here is the example code I am working with...
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.myListBox1.SelectedValueChanged += this.onchange;
}
private void Form1_Load(object sender, EventArgs e)
{
this.myListBox1.Add(new strobj("z"));
this.myListBox1.Add(new strobj("a"));
this.myListBox1.Add(new strobj("b"));
this.myListBox1.Add(new strobj("f"));
this.myListBox1.Add(new strobj("n"));
this.myListBox1.Add(new strobj("h"));
this.myListBox1.Add(new strobj("p"));
this.myListBox1.Add(new strobj("t"));
this.myListBox1.Add(new strobj("c"));
this.myListBox1.Add(new strobj("q"));
}
private void onchange(object sender, EventArgs e)
{
MessageBox.Show("Hello World");
}
int i = 0;
private void button1_Click(object sender, EventArgs e)
{
if (this.myListBox1.SelectedItem != null)
{
strobj item = (strobj)this.myListBox1.SelectedItem;
item.Name1 = i++.ToString();
}
}
}
public partial class MyListBox
{
public MyListBox()
{
InitializeComponent();
}
public void Add(strobj item)
{
item.OnNameChanged += this.MyDispalyMemberChanged;
this.Items.Add(item);
}
bool refreshing = false;
public void MyDispalyMemberChanged(strobj itemChanged)
{
this.refreshing = true;
this.RefreshItem(this.Items.IndexOf(itemChanged));
this.refreshing = false;
}
protected override void OnSelectedValueChanged(EventArgs e)
{
if (!this.refreshing)
{
base.OnSelectedValueChanged(e);
}
}
}
class strobjCollection : List<strobj>
{
NameChangeEventHandler NameChangedEvent;
}
delegate void NameChangeEventHandler(strobj sender);
public class strobj
{
internal NameChangeEventHandler OnNameChanged;
private string _Name1;
public string Name1
{
get { return this._Name1; }
set
{
this._Name1 = value;
if (this.OnNameChanged != null)
{
this.OnNameChanged(this);
}
}
}
public int i = 0;
public string str = "p";
public strobj(string name)
{
this._Name1 = name;
}
public strobj()
{
this._Name1 = "You did not create this object";
}
public override string ToString()
{
return this._Name1;
}
}
This is what the INotifyPropertyChanged interface was made for.
Instead of raising your custom event, you'd raise the PropertyChanged event with the name of the property you changed set in the event args and the listbox would update.
See MSDN.

No data is set when wpf window is loaded?

I have a issue with passing information from one wpf window to another. For some reason when main window is loaded nothing is set in the label, I need to be able to keep the data in a string to use for anything (label not important but shows what I mean)?
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public string MyData { get; set; }
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
label1.Content = MyData;
}
public partial class LoginWindow : Window
{
public LoginWindow()
{
InitializeComponent();
}
private void button2_Click(object sender, RoutedEventArgs e)
{
string mytext = "blabla";
MainWindow fromloginwindow = new MainWindow();
fromloginwindow.Mydata = mytext;
}
Or am I doing this the wrong way round?
EDIT:
Please do not go on a tangent about the label its unimportant I need to be able to get and set a string for use anywhere in the MainWindow. Also the string "mytext" is also irrelevant as obviously I will not be setting the string this way.
It sounds like you are running into an event lifecycle issue; the calls to the Loaded event happen pretty quickly and thus, the chance to set the text has passed. Instead, what you should do is either:
1) Bind the Property to the Label in the XAML
public event PropertyChangedEventHandler PropertyChanged;
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
protected string _myData = string.Empty;
public string MyData
{
get { return _myData; }
set { _myData = value; NotifyPropertyChanged("MyData"); }
}
protected void NotifyPropertyChanged(string propName)
{
var methods = PropertyChanged;
if(methods != null)
methods(this, new PropertyChangedEventArgs(propName));
}
<Label Content="{Binding MyData}" />
2) Set the control text via another method (or inside the property declaration):
public void SetLabel(string text)
{
label1.Content = text;
}
protected void button2_Click(object sender, RoutedEventArgs e)
{
MainWindow x = new MainWindow();
x.SetLabel("blabla");
}
The Loaded event occurs before you set MyData, change the code like this:
public partial class MainWindow : Window
{
public MainWindow(string data)
{
MyData = data
InitializeComponent();
}
Have you tried passing the value to the second window through the window's constructor?
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public MainWindow(string data)
: this()
{
label1.Content = data;
}
}
public partial class LoginWindow : Window
{
public LoginWindow()
{
InitializeComponent();
}
private void button2_Click(object sender, RoutedEventArgs e)
{
string mytext = "blabla";
MainWindow fromloginwindow = new MainWindow(mytext);
}
}

Categories

Resources