I have a canvas on which you can add UserControls (consists of images and textboxes)
Im trying to implement cut, copy, paste functionality on these UserControls, so the context menu is attatched to a UserControl which deals with images for example. A user right clicks here and from the context menu picks copy for instance how would I then go about implementing so they can paste this on to the canvas.
Can anyone point me in the right direction...
This can be done with RoutedCommands. A full overview is at MSDN: Commanding Overview
The short version is this: when a command source (i.e. a menu item) wants to execute a command, an event is raised. That event is handled by the nearest command binding. Cut/copy/paste commands are already included with WPF, and certain elements (namely, text boxes) already include command bindings for them.
You can define a menu item like this:
<MenuItem Header="Copy" Command="ApplicationCommands.Copy" />
And add a command binding to the UserControl like this:
<UserControl.CommandBindings>
<CommandBinding Command="ApplicationCommands.Copy"
Executed="Copy_Executed" />
</UserControl.CommandBindings>
And define the Copy_Executed method with the ExecutedRoutedEventHandler signature in the UserControl's code-behind.
Then of course do the same thing for ApplicationCommands.Paste within the canvas.
It's up to you whether you want to handle the data within your own application, or use the clipboard. If you're working with images, WPF has a Clipboard class which can work with BitmapSource objects (if you have an Image element, chances are its Source is already a BitmapSource).
Firstly, a well designed MVVM application can make copy/paste of user controls much simpler since it will turn to serialize/deserialize CLR objects to Clipboard. WPF will handle user control creation by itself after deserialization.
If your application does not implement MVVM. You can use XamlWriter/XamlReader to save user controls to XAML and recreate them by yourself. An example:
StringBuilder outstr = new StringBuilder();
//this code need for right XML fomating
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
settings.OmitXmlDeclaration = true;
XamlDesignerSerializationManager dsm =
new XamlDesignerSerializationManager(XmlWriter.Create(outstr, settings));
//this string need for turning on expression saving mode
dsm.XamlWriterMode = XamlWriterMode.Expression;
XamlWriter.Save(control, dsm);
//Read control from XAML
var frameObject = XamlReader.Parse(outstr.ToString()) as UserControl;
if (frameObject != null)
stackPanel.Children.Add(frameObject);
For the part of how to put the XAML string or serialized stream into Clipboard, you can refer to MSDN.
Hope it can help.
If you want to bind the command (as #nmclean explains) from code, you can use:
CommandBindings.Add(new CommandBinding(
ApplicationCommands.Copy,
(sender, args) => { /* logic here */ }));
Related
I have a UWP question about inheriting/ passing a event to a user control from the parent view to child.
I created a user control to display text overlays (see code below). We had a parent view that would display an overlay when the window is resized (see code below). The overlay would display the dimensions of the window when this even is triggered.
I moved the overlay to a user control and now I'm trying to pass that resized event to the overlay control. The hope is that we can register more events to the overlay control so it can display more then the resize
information. However, I'm not sure the best way to do this. My first idea was inheriting from the view, so i could just listen to the event from the overlay control, but that resulted in errors.
I believe due to the fact that the parent view has a ViewModel (i also created one for the overlay, not sure if its actually needed yet).
I have been reading about a lot of possible ways to do this, but I'm not sure which would be the best way to do this. Does anyone have any insight on this issue ? I would be open to suggestions, links, or just a general answer of
what is the best way to achieve this in our project.
Parent view
User Control
Parent Event
Control class
Some information i have been reading about:
https://documentation.devexpress.com/WPF/17449/MVVM-Framework/ViewModels/ViewModel-relationships-ISupportParentViewModel
https://learn.microsoft.com/en-us/dotnet/framework/wpf/advanced/preview-events
https://social.msdn.microsoft.com/Forums/windows/en-US/742077f6-e875-44d1-8bc4-6e6516db9eda/passing-the-parent-control-event-to-child-controls?forum=winforms
https://learn.microsoft.com/en-us/windows/uwp/xaml-platform/events-and-routed-events-overview
http://irisclasson.com/2013/12/10/passing-event-arguments-from-xaml-in-windows-store-apps-inputconverter-inputconverterparameter-etc/
https://learn.microsoft.com/en-us/windows/uwp/launch-resume/how-to-create-and-consume-an-app-service
Update
Adding the viewModel to the parent viewModel (terminal), and passing it to the control via the Datacontext did not work
As you're already using MVVM, I'd recommend going the full route utilizing "Interactivity", "Commands", and "child ViewModels". This is a commonly used patter in MVVM WPF applications, and can be applied to UWP apps as well.
Using "Interactivity" and interactions
The interactivity / behaviors library from Microsoft allows you to bind events in XAML to an ICommand in the ViewModel. You can get the managed NuGet package here.
From the official examples on GitHub, shortened:
<Button x:Name="button1" Content="Increment">
<Interactivity:Interaction.Behaviors>
<Interactions:EventTriggerBehavior EventName="Click" SourceObject="{Binding ElementName=button1}">
<Interactions:InvokeCommandAction Command="{Binding UpdateCountCommand}"/>
</Interactions:EventTriggerBehavior>
</Interactivity:Interaction.Behaviors>
</Button>
Forward command data to child ViewModel
Having this event now routed to your command in your parent ViewModel, you can now either call your overlay ViewModel and pass the info directly to it:
private readony IOverlayViewModel _overlayViewModel;
public ICommand UpdateCountCommand { get; set; }
ctor(IOverlayViewModel overlayViewModel)
{
_overlayViewModel = overlayViewModel;
UpdatedCountCommand = new MyICommandImplementation(UpdatedCountCommand_Executed);
}
private void UpdatedCountCommand_Executed(/* Add correct method signature */)
{
// If needed, retrieve data from parameter...
// Update overlay ViewModel text
_overlayViewModel.Text = ""; // Whichever text was calculated before
}
Or you use a messenger (mediator pattern) to send it to an overlay.
I was misusing the bindings. x:Bind and Binding are using different types of context. For this binding to work we would need to set the parent's element Datacontext to 'this'. x:Bind on the other hand does this implicitly.
<views:OverlayView DataContext="{x:Bind ViewModel.Overlay}"></views:OverlayView>
How i can make this buttons with icons like these ??
Normally, in order to create stylized buttons in Winforms, you have to work with the Button.Image property:
this.myButton.FlatStyle = FlatStyle.Flat;
this.myButton.Image = // your image
// ...
The buttons in your image include a text, but since the enumeration System.Windows.Forms.TextImageRelation (used in the property Button.TextImageRelation) does not allow for a text below the image... obtaining the same style with this approach risks to become a hard task (using GDI, handling painting events, creating a derivative of the Button class, etc...), unless there is a trick that I don't know.
The fastest and simplest way to recreate the same style is to create a button with empty Text property and manually include the text in the image file. Actually, you set:
this.myButton.FlatStyle = FlatStyle.Flat;
this.myButton.ImageAlign = ContentAlignment.MiddleCenter;
this.myButton.Text = "";
and you put this image (just an example) straight into the control:
I am using MVVM in my project and I am implementing the Diagram Designer and I am using RAD Diagram control of Telerik.I am dragging the shape from my RadToolBox to canvas for which I have implemented the event.Now on this event I want to save the diagram in RavenDB once the shape has been dropped on Canvas ( Ruler).This whole thing is in the User control stored in one library and I am creating the reference of this library in my actual WPF form which is in other library.My requirement is I want to call the Save command which is present in View Model of WPF form, from my xaml.cs of the user control and this user control is present in other library.
How shall i achieve the same.Please help.
Why do have multiple assemblies? If you don't need them, then make your life simpler and just use NameSpaces to separate your controls from your forms in the same assembly.
Right now that's said :-)
The easy way is via an Event. Add an event to your UserControl, and then in the Window add the code when the event happens.
Have a read through MSDN Events
I really dont Why you would need to call a save command from another WindowsForm.
Telerik UI Diagram provides indefined save Command which is used to save the Diagram,
InXAML,
<Grid.CommandBindings>
<CommandBinding Command="telerik:DiagramCommands.Save" Executed="ExecuteSave" />
</Grid.CommandBindings>
And In code behind define save function,
private void ExecuteSave(object sender, ExecutedRoutedEventArgs e)
{
e.Handled = true;
this.diagram.save();
}
I have an Xml file which tells me the controls that I have to add to a form but this Xml changes dynamically and I need to update the form.
Currently, I can read the XML file, but I dont know if it is possible to automatically create forms based on that or not ?
Yes It is possible.
WPF offers several ways of creating controls either in Xaml or in code.
For your case if you need to dynamically create your controls, you'll have to create them in code. You can either create your control directly using their constructors as in:
// Create a button.
Button myButton= new Button();
// Set properties.
myButton.Content = "Click Me!";
// Add created button to a previously created container.
myStackPanel.Children.Add(myButton);
Or you could create your controls as a string containing xaml and use a XamlReader to parse the string and create the desired control:
// Create a stringBuilder
StringBuilder sb = new StringBuilder();
// use xaml to declare a button as string containing xaml
sb.Append(#"<Button xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' ");
sb.Append(#"Content='Click Me!' />");
// Create a button using a XamlReader
Button myButton = (Button)XamlReader.Parse(sb.ToString());
// Add created button to previously created container.
stackPanel.Children.Add(myButton);
Now for which one of the two methods you want to use really depends on you.
Jean-Louis
You can easily add controls via code in wpf, you can follow this article. Another thing worth noting is that XAML is a form of XML so you can you save your XAML to an XML file, that way you wouldn't need to add controls in code, however it depends on the complexity of your application.
I am pretty new to Xaml but to add to Jean-Louis's answer if you do not want to add the namespaces to every element string then you can do something like this using the System.Windows.Markup namespace:
ParserContext context = new ParserContext();
context.XmlnsDictionary.Add("", "http://schemas.microsoft.com/winfx/2006/xaml/presentation");
context.XmlnsDictionary.Add("x", "http://schemas.microsoft.com/winfx/2006/xaml");
string xaml = String.Format(#"<ListBoxItem Name='Item{0}' Content='{1}' />", itemID, listItems[itemID]);
UIElement element = (UIElement)XamlReader.Parse(xaml, context);
listBoxElement.Items.Add(element);
Adding controls through the Children.Add method is the quickest way i've found, such as for example
this.Grid.Add(new TextBox() { Text = "Babau" });
I just found myself a new challenge:
Make a Word Processor that is in handling more like the web than plain text.
Designing a nice framework for this is what i cant wait to start with, but i do need to know what the possibilities are at the GUI side (it will probably have loads of GUI challenges).
So the basic thing that I need some sort of Control where I can make parts of my text clickable / mouse-over-able.
I'm kinda new to WPF and not sure how to do this.
Has anybody an idea how to make this?
Are there examples?
Are there already controls for this?
Thanks in advance
EDIT:
I found out some way to do it with a richtextbox:
// Create a FlowDocument to contain content for the RichTextBox.
FlowDocument myFlowDoc = new FlowDocument();
// Add paragraphs to the FlowDocument.
Hyperlink myLink = new Hyperlink();
myLink.Inlines.Add("hyperlink");
myLink.NavigateUri = new Uri("http://www.stackoverflow.com");
// Create a paragraph and add the Run and hyperlink to it.
Paragraph myParagraph = new Paragraph();
myParagraph.Inlines.Add("check this link out: ");
myParagraph.Inlines.Add(myLink);
myFlowDoc.Blocks.Add(myParagraph);
// Add initial content to the RichTextBox.
richTextBox1.Document = myFlowDoc;
I now get a nice hyperlink in my textbox... except when i click it, nothing happens.
what am i missing here?
You can use the Hyperlink class. It's a FrameworkContentElement, so you can use it in a TextBlock or FlowDocument or anywhere else you can embed content.
<TextBlock>
<Run>Text</Run>
<Hyperlink NavigateUri="http://stackoverflow.com">with</Hyperlink>
<Run>some</Run>
<Hyperlink NavigateUri="http://google.com">hyperlinks</Hyperlink>
</TextBlock>
You may want to look at using a RichTextBox as part of your editor. This will host a FlowDocument, which can contain content such as Hyperlinks.
Update: There are two ways to handle clicks on the Hyperlink. One is to handle the RequestNavigate event. It is a Routed Event, so you can either attach a handler to the Hyperlink itself or you can attach one to an element higher in the tree such as the Window or the RichTextBox:
// On a specific Hyperlink
myLink.RequestNavigate +=
new RequestNavigateEventHandler(RequestNavigateHandler);
// To handle all Hyperlinks in the RichTextBox
richTextBox1.AddHandler(Hyperlink.RequestNavigateEvent,
new RequestNavigateEventHandler(RequestNavigateHandler));
The other way is to use commanding by setting the Command property on the Hyperlink to an ICommand implementation. The Executed method on the ICommand will be called when the Hyperlink is clicked.
If you want to launch a browser in the handler, you can pass the URI to Process.Start:
private void RequestNavigateHandler(object sender, RequestNavigateEventArgs e)
{
Process.Start(e.Uri.ToString());
}
Note you also need to set the following properties on your RichTextBox or the hyperlinks will be disabled and won't fire off events. Without IsReadOnly you need to Ctrl-click the hyperlinks, with IsReadOnly they fire with a regular left-click.
<RichTextBox
IsDocumentEnabled="True"
IsReadOnly="True">
The simplest way is to handle RequestNavigate event like this:
...
myLink.RequestNavigate += HandleRequestNavigate;
...
private void HandleRequestNavigate(object sender, RoutedEventArgs e)
{
var link = (Hyperlink)sender;
var uri = link.NavigateUri.ToString();
Process.Start(uri);
e.Handled = true;
}
There are some issues with starting a default browser by passing url to the Process.Start and you might want to google for a better way to implement the handler.