How to receive the InkCanvas.StrokeCollected event in the view model - wpf

In using MVVM pattern, I have a custom inkcanvas with:
protected override void OnStrokeCollected(InkCanvasStrokeCollectedEventArgs e)
{
CustomStroke newStroke = new CustomStroke(e.Stroke.StylusPoints, e.Stroke.DrawingAttributes);
this.Strokes.Remove(e.Stroke);
this.Strokes.Add(newStroke);
InkCanvasStrokeCollectedEventArgs eNew = new InkCanvasStrokeCollectedEventArgs(newStroke);
// Raises the System.Windows.Controls.InkCanvas.StrokeCollected event.
base.OnStrokeCollected(eNew);
}
How do I get the view model to receive the InkCanvas.StrokeCollected event?
I can not bind the XAML to the strokes as the StrokeCollection.CollectionChanged event will be called three times by the custom inkcanvas.
Any help is appreciated.

Try this
public Window3()
{
InitializeComponent();
var vm=new ViewModel();
this.DataContext = vm;
canvas.StrokeCollected += vm.OnStrokeCollected;
}
ViewModel
public class ViewModel
{
public void OnStrokeCollected(object sender, InkCanvasStrokeCollectedEventArgs e)
{
}
}
Edit
if you want to do it without codebehind see the article EventTrigger

You simply bind it via XAML as you already did, which is the correct way to do it.
That you get 3 events, doesn't matter. Just handle the one you need.
For example, if you are only interested in the StrokeCollectedEvent, then just do
protected override void OnStrokeCollected(InkCanvasStrokeCollectedEventArgs e)
{
if(e.RoutedEvent != InkCanvas.StrokeCollectedEvent)
return;
// handle the event
}
For a full list of Events, consult the "Fields" Section of InkCanvas MSDN documentation. The fields ending with "Event" are RoutedEvent constants, which are passed in the InkCanvasStrokeCollectedEventArgs.

Related

Creating property for WPF user control datagrid

I have a user control with a datagrid called IGrid. I want to add GridViewColumnCollection poperty for it.
public class DataGridNumericColumn : DataGridTextColumn
{
protected override object PrepareCellForEdit(System.Windows.FrameworkElement editingElement, System.Windows.RoutedEventArgs editingEventArgs)
{
TextBox edit = editingElement as TextBox;
edit.PreviewTextInput += OnPreviewTextInput;
return base.PrepareCellForEdit(editingElement, editingEventArgs);
}
void OnPreviewTextInput(object sender, System.Windows.Input.TextCompositionEventArgs e)
{
try
{
Convert.ToInt32(e.Text);
}
catch
{
e.Handled = true;
}
}
}
From Google i got a piece of code `
private Collection<DataGridColumn> field = new Collection<DataGridColumn>();
[Category("Data")]
[Description("Column Creation")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public Collection<DataGridColumn> Columns
{
get { return field; }
}
Here I can get GridViewColumnCollection in visual tree ,
My question is how to add a new type (DataGridNumericColumn )in the collection using the above code.
If you do not know what the code does, it is probably best to research it before attempting to use it or come up with your own solution. As far as I can tell, you are attempting to add an additional property to a UserControl that inherits DataGrid.
You have two options:
Create a DependencyProperty (the best choice, in my opinion).
Create a normal property
Here's a couple links to help you get started:
What is the difference between Property and Dependency Property
When should I use dependency properties in WPF?

How to access ShowMessageAsync method of MetroWindow from ViewModel

I am using MahApps.metro WPF library with MVVM. I have a ViewModel from which I need to display a Dialog. The MetroWindow has ShowMessageAsync. But what is the proper way to access it from the ViewModel? As I understand I need a View instance but passing that into the ViewModel doesn't seem like a good approach.
Use following approach:
Take an Action<T> ShowMessageAsync in your ViewModel which you are binding with window.
Now create a behaviour for Window and use following code in behaviour
protected override void OnAttached()
{
base.OnAttached();
this.AssociatedObject.Loaded += AssociatedObject_Loaded;
}
void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
{
if (this.AssociatedObject.DataContext is WindowViewModel)
{
WindowViewModel vm = this.AssociatedObject.DataContext as WindowViewModel;
vm.ShowMessageAsync = OnShowMessageAsync;
}
}
private void OnShowMessageAsync(T param)
{
//Write your logic to call ShowMessageAsync method.
}
Now in this way, from the ViewModel of your MainWindow you will have ability to open another child window.

How can I bind to a non ObservableCollection?

Is there a way to bind directly to a Collection in the model and manually tell WPF that the binding needs refreshing without having to create an ObservableCollection for it in the viewmodel?
<ListBox ItemsSource="{Binding Position.PossibleMoves}">
...
</ListBox>
Position is my model, part of a chess library, and PossibleMoves is a Collection within it. I do not want to implement INotifyProperty changed or put ObservableCollections in a stand alone optimized library.
I want to avoid copying PossibleMoves into an ObservableCollection every time the position is updated. The data binding works on initialization but it would be handy if I could also refresh the binding at will inside the viewmodel.
Calling OnNotifyPropertyChanged("Position.PossibleMoves") from the viewmodel doesn't work because the reference to the collection itself does not change.
You can do this by using an attached behavior to bind a handler to an event that gets triggered in the view model. You can't bind directly to events though so you have to wrap them in a class like so:
public class Refresher
{
public delegate void RefreshDelegate();
public event RefreshDelegate Refresh;
public void DoRefresh()
{
if (this.Refresh != null)
this.Refresh();
}
}
Now add an instance of that to your view model:
public class MyViewModel
{
public IList<string> Items { get; set; }
private Refresher _Refresher = new Refresher();
public Refresher Refresher {get {return this._Refresher;}}
}
Next create an attached behavior that registers a delegate instance with that event and forces the listbox to refresh its binding:
public static class RefreshBehavior
{
public static readonly DependencyProperty RefresherProperty = DependencyProperty.RegisterAttached(
"Refresher",
typeof(Refresher),
typeof(RefreshBehavior),
new PropertyMetadata(null, OnRefresherChange));
public static void SetRefresher(DependencyObject source, Refresher value)
{
source.SetValue(RefresherProperty, value);
}
public static Refresher GetRefresher(DependencyObject source)
{
return (Refresher)source.GetValue(RefresherProperty);
}
private static void OnRefresherChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Refresher.RefreshDelegate handler = () =>
{
var listBox = d as ListBox;
listBox.Items.Refresh();
};
if (e.NewValue != null)
(e.NewValue as Refresher).Refresh += handler;
if (e.OldValue != null)
(e.OldValue as Refresher).Refresh -= handler;
}
}
And finally attach it to your listbox in the xaml:
<ListBox ItemsSource="{Binding Items}"
local:RefreshBehavior.Refresher="{Binding Refresher}"/>
That's it. Call Refresher.DoRefresh() in your view model and it will force a listbox update.
This works but it's really hammering a square peg into a round hole. If I were you I'd do everything I could to try and do proper collection changed notification in your view model. I understand you wanting to keep ObservableCollection out of your model but there are ways to proxy change notification automatically (e.g. Castle DynamicProxy).
You need to NotifyPropertyChange for the PossibleMoves from inside the Position class or make a property that delegates to the Position.PossibleMoves and notify that one.

RaiseCanExecuteChanged event

I am at the stage in a project where I need to get control of enabling / disabling some hyperlinks based on various business rules. I noticed all topics on RaiseCanExecuteChanged event refer to MVVM light. Does this mean that I have to use MVVM light or is there a way to access this event using standard MVVM. If so, how? Thanks
ICommands have an event that command watchers subscribe to. When this event fires, it is the responsibility of the watchers (Button, etc) to call CanExecute in order to determine if they should enable/disable themselves.
As you must implement ICommand, you must also provide a way for your ViewModels (or whatever, depending on your design) to fire this event from outside the ICommand instance. How you go about this is up to you. It is common (in my experience) to place a method on your ICommand implementation called something like FireCanExecuteChanged which you can call to inform the instance that they should fire the CanExecute event.
Here's an example in vaguely c#-like pseudocode.
public sealed class MyViewModel
{
// dependencyproperty definition left off for brevity
public MyCommand ACommand {get;private set;}
// fired when some DP changes which affects if ACommand can fire
private static void OnSomeDependencyPropertyChanged
(object sender, EventArgs e)
{
(sender as MyViewModel).ACommand.FireCanExecuteChanged();
}
}
public sealed class MyCommand : ICommand
{
public event EventHandler CanExecuteChanged;
public bool CanExecute(object arg) { return arg != null; }
public void Execute(object arg) { throw new NotImplementedException(); }
public void FireCanExecuteChanged() {
CanExecuteChanged(this, EventArgs.Empty); }
}

Expose a Click event of a button inside a UserControl in Silverlight

I have a button inside my UserControl. I have three instances of this UserControl on the same page.
How can I expose the click event of the button inside such that I can assign different events for each instance of my UserControl.
I think this is similar to concept behind exposing DependencyProperty but I don't understand how to do it for events.
Thanks.
I normally add an event of the same name (and same parameters) to the user control and subscribe to the child control's original event, so I can pass the event on:
public partial class ClickEventControl : UserControl
{
public event EventHandler<RoutedEventArgs> Click;
public ClickEventControl()
{
InitializeComponent();
}
private void aButton_Click(object sender, RoutedEventArgs e)
{
if (Click != null)
{
Click(sender, e);
}
}
}
I would also be interested if there is a more general way of doing it.

Resources