Registering RoutedEventHandler in UWP template control - xaml

I'm having difficulty finding how to register a RoutedEventHandler in UWP. I'm attempting to code a template control that has event properties similar to ContentDialog's:
PrimaryButtonClick="ClickEvent"
Where ClickEvent is defined in the cs file. I'm only just getting the hang of templates, but I believe I want to do something that looks like this:
<Button Content="{TemplateBinding PrimaryButtonText}" Click="{TemplateBinding PrimaryButtonClick}"/>
Currently, all I can find is references to WPF versions of this type of code:
public static readonly RoutedEvent ValueChangedEvent =
EventManager.RegisterRoutedEvent("ValueChanged",
RoutingStrategy.Direct, typeof(RoutedPropertyChangedEventHandler<double>),
typeof(NumericBox));
public event RoutedPropertyChangedEventHandler<double> ValueChanged
{
add { AddHandler(ValueChangedEvent, value); }
remove { RemoveHandler(ValueChangedEvent, value); }
}
private void OnValueChanged(double oldValue, double newValue)
{
RoutedPropertyChangedEventArgs<double> args =
new RoutedPropertyChangedEventArgs<double>(oldValue, newValue);
args.RoutedEvent = NumericBox.ValueChangedEvent;
RaiseEvent(args);
}
But of course the types have changed. Can someone point me in the right direction?

Unfortunately, the concept of RoutedEvent (bubbling, tunneling) is not available in UWP currently. You can just create a classic event however instead:
public event EventHandler PrimaryButtonClick;
protected void InnerButton_Click(object sender, EventArgs e)
{
PrimaryButtonClick?.Invoke( sender, e );
}
Bubbling of events is possible for some predefined events, but it is not yet possible to allow bubbling for custom events in current version of UWP.

Related

How to access code behind from Tablet/Phone template?

I am taking over a project with a DashboardPage and a DashboardPageViewModel that are linked through DI's ViewModelLocator. The Dashboard page have the following code that separates the Xaml/Code behinds into two separate templates.
private void SetContent()
{
Debug.WriteLine("Dashboardpage setContent");
switch(Device.Idiom)
{
case TargetIdiom.Phone:
Content = new PrimaryPhoneLayout
{
RegionContent = RegionContent
};
break;
case TargetIdiom.Tablet:
Content = new PrimaryTabletLayout(deviceDisplay)
{
RegionContent = RegionContent
};
break;
default:
throw new NotSupportedException($"{Device.Idiom} is not a supported idom");
}
}
I want to add a button in both Phone/Tablet xaml and handle the logic within DashboardPage. How can I make a reference to Dashboard page when these XAML files are linked to their individual code behind and not Dashboard Page?
To elaborate further, DashboardPage derives from MenuContainerPage that allows me to slide in/out of my slide menu. I want to handle this logic through a button that I implemented in both Tablet/Phone layout.
This is how I would do it.
I would start by creating an interface with the events I want to expose from my ContentView
For the sample, I will call this interface as IMenuOptionHandler and it would look like this
public interface IMenuOptionHandler
{
event EventHandler OnSlideIn;
event EventHandler OnSlideOut;
}
Here we have two events that will be invoked from our ContentViews. You can add as many as you wish.
Then we need to make our ContentViews to implement this interface:
public partial class PrimaryPhoneLayouts : ContentView, IMenuOptionHandler
{
//...
#region "IMenuOptionHandler implementation"
public event EventHandler OnSlideIn;
public event EventHandler OnSlideOut;
#endregion
void OnSlideInButtonClicked(object sender, EventArgs e)
{
OnSlideIn?.Invoke(this, EventArgs.Empty);
}
void OnSlideOutButtonClicked(object sender, EventArgs e)
{
OnSlideOut?.Invoke(this, EventArgs.Empty);
}
}
public partial class PrimaryTabletLayout : ContentView, IMenuOptionHandler
{
// ...
#region "IMenuOptionHandler implementation"
public event EventHandler OnSlideIn;
public event EventHandler OnSlideOut;
#endregion
void OnSlideInButtonClicked(object sender, EventArgs e)
{
OnSlideIn?.Invoke(this, EventArgs.Empty);
}
void OnSlideOutButtonClicked(object sender, EventArgs e)
{
OnSlideOut?.Invoke(this, EventArgs.Empty);
}
As you can see both classes are implementing our interface.
Also, I added two sets of methods which are the methods that you will hook to the Buttons on the XAML.
Let's imagine that your XAML looks like this:
<ContentView.Content>
<StackLayout Orientation="Vertical"
HorizontalOptions="FillAndExpand">
<Button Text="SlideIn"
VerticalOptions="CenterAndExpand"
HorizontalOptions="FillAndExpand"
Clicked="OnSlideInButtonClicked" />
<Button Text="SlideOut"
VerticalOptions="CenterAndExpand"
HorizontalOptions="FillAndExpand"
Clicked="OnSlideOutButtonClicked" />
</StackLayout>
</ContentView.Content>
Both XAML should have the buttons and the Clicked events wired to our methods in the Code behind classes.
These two methods the only purpose (as of now) is to invoke the events and notify anyone that it's subscribed to them that an event happened.
Now in your DashboardPage
you will add this global property for simplicity
IMenuOptionHandler MenuOptionHandler => Content as IMenuOptionHandler;
This will cast the Content of the Page, whatever it's the value, to IMenuOptionHandler. Any class that implements this interface will allow this cast to happen.
The last part to add on the same DashboardPage is the subscription to the events. These are gonna happen in the OnAppearing method and we will be unsubscribing on the OnDisappearing.
protected override void OnAppearing()
{
base.OnAppearing();
if (MenuOptionHandler != null)
{
MenuOptionHandler.OnSlideIn += MenuOptionHandler_OnSlideIn;
MenuOptionHandler.OnSlideOut += MenuOptionHandler_OnSlideOut;
}
}
protected override void OnDisappearing()
{
base.OnDisappearing();
if (MenuOptionHandler != null)
{
MenuOptionHandler.OnSlideIn -= MenuOptionHandler_OnSlideIn;
MenuOptionHandler.OnSlideOut -= MenuOptionHandler_OnSlideOut;
}
}
void MenuOptionHandler_OnSlideIn(object sender, EventArgs e)
{
//Logic to handle the SlideIn
Debug.WriteLine("MenuOptionHandler_OnSlideIn");
}
void MenuOptionHandler_OnSlideOut(object sender, EventArgs e)
{
//Logic to handle the SlideOut
Debug.WriteLine("MenuOptionHandler_OnSlideOut");
}
Now, whenever one of the Buttons on the ContentView (iPhone or Tablet) is clicked, the Dashboard ContentPage will be notified about this and you will be able to perform any task you wish.
Hope this helps.-
Assume you have a button in Page1, first give a name to the Button in Xaml:
<Button x:Name="btnInPage1" Text="Welcome to Xamarin.Forms!" />
In the code behind of Page1, create a public static property of button, and set the btnPageOne = btnInPage1:
public partial class Page1 : ContentPage
{
public static Button btnPageOne;
public Page1 ()
{
InitializeComponent ();
btnPageOne = btnInPage1;
}
}
Then in your DashboardPage, you can access the button by using Page1.btnPageOne, and handle the logic with:
Page1.btnPageOne.Clicked += delegate {
Console.WriteLine("Page1 btn clicked");
};
The same if you have Page2, Page3...

OxyPlot: Keep tracker open when left button released

I'm using OxyPlot 2014.1.546 with C# and WPF.
My plot has a custom tracker that appears when the user clicks a point. I'd like to include buttons for performing actions related to the clicked point. Adding them to the tracker template is straightforward enough; the problem is, by default, the tracker disappears as soon as the user releases the mouse button, which means it's impossible to actually click them.
Is there any way to tell OxyPlot to keep the tracker open until the user clicks outside of it?
The short answer is that OxyPlot doesn't appear to support this behavior directly. After spending some time digging through the decompiled source, I came up with the following solution, which appears to work. The basic idea was to derive my own StayOpenTrackerManipulator from OxyPlot's built-in TrackerManipulator and instantiate it in response to a click. My manipulator overrides the virtual Completed() function, which the framework calls when the mouse button is released, and defers the call to the base-class Completed(), which closes the tracker, until the next time the mouse is clicked (or until the plot is modified, or until the mouse leaves it). Since I'm using C# and WPF, I wrapped everything up in an attached behavior that can be used from XAML like so:
<PlotView behaviors:ShowTrackerAndLeaveOpenBehavior.BindToMouseDown="Left" />
but it would be simple enough to pull the guts out and reuse them in a different manner if needed. Here's the source:
/// <summary>
/// Normal OxyPlot behavior is to show the tracker when the bound mouse button is pressed,
/// and hide it again when the button is released. With this behavior set, the tracker will stay open
/// until the user clicks the plot outside it (or the plot is modified).
/// </summary>
public static class ShowTrackerAndLeaveOpenBehavior
{
public static readonly DependencyProperty BindToMouseDownProperty = DependencyProperty.RegisterAttached(
"BindToMouseDown", typeof(OxyMouseButton), typeof(ShowTrackerAndLeaveOpenBehavior),
new PropertyMetadata(default(OxyMouseButton), OnBindToMouseButtonChanged));
[AttachedPropertyBrowsableForType(typeof(IPlotView))]
public static void SetBindToMouseDown(DependencyObject element, OxyMouseButton value) =>
element.SetValue(BindToMouseDownProperty, value);
[AttachedPropertyBrowsableForType(typeof(IPlotView))]
public static OxyMouseButton GetBindToMouseDown(DependencyObject element) =>
(OxyMouseButton) element.GetValue(BindToMouseDownProperty);
private static void OnBindToMouseButtonChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (!(d is IPlotView plot))
throw new InvalidOperationException($"Can only be applied to {nameof(IPlotView)}");
if (plot.ActualModel == null)
throw new InvalidOperationException("Plot has no model");
var controller = plot.ActualController;
if (controller == null)
throw new InvalidOperationException("Plot has no controller");
if (e.OldValue is OxyMouseButton oldButton && oldButton != OxyMouseButton.None)
controller.UnbindMouseDown(oldButton);
var newButton = GetBindToMouseDown(d);
if (newButton == OxyMouseButton.None)
return;
controller.UnbindMouseDown(newButton);
controller.BindMouseDown(newButton, new DelegatePlotCommand<OxyMouseDownEventArgs>(
AddStayOpenTrackerManipulator));
}
private static void AddStayOpenTrackerManipulator(IPlotView view, IController controller,
OxyMouseDownEventArgs e)
{
controller.AddMouseManipulator(view, new StayOpenTrackerManipulator(view), e);
}
private class StayOpenTrackerManipulator : TrackerManipulator
{
private readonly PlotModel _plotModel;
private bool _isTrackerOpen;
public StayOpenTrackerManipulator(IPlotView plot)
: base(plot)
{
_plotModel = plot?.ActualModel ?? throw new ArgumentException("Plot has no model", nameof(plot));
Snap = true;
PointsOnly = false;
}
public override void Started(OxyMouseEventArgs e)
{
_plotModel.TrackerChanged += HandleTrackerChanged;
base.Started(e);
}
public override void Completed(OxyMouseEventArgs e)
{
if (!_isTrackerOpen)
{
ReallyCompleted(e);
}
else
{
// Completed() is called as soon as the mouse button is released.
// We won't call the base Completed() here since that would hide the tracker.
// Instead, defer the call until one of the hooked events occurs.
// The caller will still remove us from the list of active manipulators as soon as we return,
// but that's good; otherwise the tracker would continue to move around as the mouse does.
new DeferredCompletedCall(_plotModel, () => ReallyCompleted(e)).HookUp();
}
}
private void ReallyCompleted(OxyMouseEventArgs e)
{
base.Completed(e);
// Must unhook or this object will live as long as the model (instead of as long as the manipulation)
_plotModel.TrackerChanged -= HandleTrackerChanged;
}
private void HandleTrackerChanged(object sender, TrackerEventArgs e) =>
_isTrackerOpen = e.HitResult != null;
/// <summary>
/// Monitors events that should trigger manipulator completion and calls an injected function when they fire
/// </summary>
private class DeferredCompletedCall
{
private readonly PlotModel _plotModel;
private readonly Action _completed;
public DeferredCompletedCall(PlotModel plotModel, Action completed)
{
_plotModel = plotModel ?? throw new ArgumentNullException(nameof(plotModel));
_completed = completed ?? throw new ArgumentNullException(nameof(completed));
}
/// <summary>
/// Start monitoring events. Their observer lists will keep us alive until <see cref="Unhook"/> is called.
/// </summary>
public void HookUp()
{
Unhook();
_plotModel.MouseDown += HandleMouseDown;
_plotModel.Updated += HandleUpdated;
_plotModel.MouseLeave += HandleMouseLeave;
}
/// <summary>
/// Stop watching events. If they were the only things keeping us alive, we'll turn into garbage.
/// </summary>
private void Unhook()
{
_plotModel.MouseDown -= HandleMouseDown;
_plotModel.Updated -= HandleUpdated;
_plotModel.MouseLeave -= HandleMouseLeave;
}
private void CallCompletedAndUnhookEvents()
{
_completed();
Unhook();
}
private void HandleUpdated(object sender, EventArgs e) => CallCompletedAndUnhookEvents();
private void HandleMouseLeave(object sender, OxyMouseEventArgs e) => CallCompletedAndUnhookEvents();
private void HandleMouseDown(object sender, OxyMouseDownEventArgs e)
{
CallCompletedAndUnhookEvents();
// Since we're not setting e.Handled to true here, this click will have its regular effect in
// addition to closing the tracker; e.g. it could open the tracker again at the new position.
// Modify this code if that's not what you want.
}
}
}
}

Event for cell add(jgraphx)

I need to make a action when the user make a new cell(drag and drop a cell from editorPallete).
graphComponent.addListener(mxEvent.ADD, new mxEventSource.mxIEventListener() {
#Override
public void invoke(Object sender, mxEventObject evt) {
System.out.println("event add");
}
} );
I do not receive any event for mxEvent.ADD, same result for mxEvent.ADD_CELLS.
You need to add the listener to the graph, not the graphComponent. You also need to use the CELLS_ADDED event instead of the ADD event. You can take a look at the api documentation for the mxGraph class to view a list of fired events for the class: http://jgraph.github.io/mxgraph/docs/js-api/files/view/mxGraph-js.html --> scroll down to the Events section
So your code should look something like this:
graphComponent.getGraph().addListener(mxEvent.CELLS_ADDED, new xEventSource.mxIEventListener() {
#Override
public void invoke(Object sender, mxEventObject evt) {
System.out.println("event add");
}
} );
Hope this helps,

Can you create an event in a user control, if so, how?

I have created a User Control containing a DatePicker and I want create an event in the user control linking to the DatePicker DateChanged event. This custom user control will be used in an itemscontrol.
Yes. Add a public event to the control. And then add a method that looks for delegates attached to the event. If there are any delegates, raise the event. Here's an example:
In the User Control:
public partial class Controls_UserComments : System.Web.UI.UserControl
{
// the event delegates may listen for
public event EventHandler CommentEditing;
protected void Page_Load(object sender, EventArgs e)
{
// handle an event in the user control and bubble the event up to any delegates
GridView_Comments.RowCancelingEdit += new GridViewCancelEditEventHandler(GridView_Comments_RowCancelingEdit);
}
void GridView_Comments_RowCancelingEdit(object sender, GridViewCancelEditEventArgs e)
{
GridView_Comments.EditIndex = -1;
GridView_Comments.DataBind();
// raise the event for attached delegates
if (CommentEditing != null)
CommentEditing(this, EventArgs.Empty);
}
}
Now, in the web form utilizing the user control:
<ppc:UserComments ID="UserComments_ObservationComments" runat="server"
OnCommentEditing="RefreshComments"
/>
Good luck!

Editing Data in Child Window with RIA Services and Silverlight 4

Is it possible to edit data in a SilverLight Child window when using RIA Services and Silverlight 4? It sounds like a simple enough question, but I have not been able to get any combination of scenarios to work.
Simply put, I am viewing data in a grid that was populated through a DomainDataSource. Instead of editing the data on the same screen (this is the pattern that ALL of the Microsoft samples seem to use), I want to open a child window, edit the data and return. Surely this is a common design pattern.
If anyone knows of a sample out there that uses this pattern, a link would be much appreciated.
Thanks,
Rick Arthur
This is a Microsoft sample that uses a ChildWindow. It uses RIA services, but not MVVM.
It doesn't fix a problem I'm having where entities get attached to my context before I want them to be, but does what you're looking for other than that.
Here's the relevant code to save you downloading the zip:
private void addNewEmployee_Click(object sender, RoutedEventArgs e)
{
EmployeeRegistrationWindow addEmp = new EmployeeRegistrationWindow();
addEmp.Closed += new EventHandler(addEmp_Closed);
addEmp.Show();
}
public partial class EmployeeRegistrationWindow : ChildWindow
{
public EmployeeRegistrationWindow()
{
InitializeComponent();
NewEmployee = new Employee();
addEmployeeDataForm.CurrentItem = NewEmployee;
addEmployeeDataForm.BeginEdit();
}
private void OKButton_Click(object sender, RoutedEventArgs e)
{
addEmployeeDataForm.CommitEdit();
this.DialogResult = true;
}
private void CancelButton_Click(object sender, RoutedEventArgs e)
{
NewEmployee = null;
addEmployeeDataForm.CancelEdit();
this.DialogResult = false;
}
public Employee NewEmployee { get; set; }
}
The MVVM light Toolkit found here has messeging between viewmodels for more information check above site. Please write if u need an example.