WPF ListView - How to copy individual cells - wpf

I have code where I can take a look at the SelectedItem and then output ToString() to get the record into the clipboard.
How can I detect what cell the user is right clicking on in order to copy just that cell in the SelectedItem?
For example, if I have Borrower Information and the user right-clicks on last name, I would like to give the ability to just copy last name to clipboard.
Thank you!
UPDATE:
Here is the code that I used as suggested by Josh, it worked great:
private void BorrowerInfoCopyClicked(object sender, RoutedEventArgs e)
{
BorrowerViewModel vm = this.DataContext as BorrowerViewModel;
if (vm != null)
{
Clipboard.SetData(DataFormats.Text, vm.CurrentTextBlockText);
}
}
private void AddressCopyClicked(object sender, RoutedEventArgs e)
{
BorrowerViewModel vm = this.DataContext as BorrowerViewModel;
if (vm != null)
{
Clipboard.SetData(DataFormats.Text, vm.CurrentTextBlockText);
}
}
private void lstViews_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
BorrowerViewModel vm = this.DataContext as BorrowerViewModel;
if (vm != null)
{
if (e.OriginalSource is TextBlock)
{
TextBlock txtBlock = e.OriginalSource as TextBlock;
vm.CurrentTextBlockText = txtBlock.Text;
}
}
}

I've done this by handling the PreviewMouseRightButtonDown event on the ListView and checking if e.OriginalSource is a TextBlock. If so, copy the txtBlk.Text to the clipboard. This code could either be in the code-behind of the View that contains the ListView, or as a behavior you attach to the ListView. If you need to use a context menu to perform the Copy operation, have a TextBlock field that you use to store a reference to the TextBlock, and in your method that responds to a MenuItem's click (or Command execution) reference the TextBlock there instead.

Related

Deselecting item from Silverlight datagrid if it's clicked twice

I have a Silverlight 4.0 datagrid, which has the SelectionMode set to Single. The problem with this is that users need to CTRL+Click on an already-selected row in order to deselect it (and have nothing selected in the grid). I'd like for them to be able to simply left-click on the already-selected row to have it deselected.
I tried doing this with a SelectionChanged event (inspecting the added items in the event arguments), however it didn't work because the event isn't thrown when the user clicks on the same row twice.
Any advice?
There is no way to capture the second event because it is never fired. What you could do is apply the type of customization used in this project to one that does capture the second click and fire the event a second time should you wish:
http://www.codeproject.com/KB/silverlight/doubleClickDataGridSL.aspx
I have the same task, so here is my solution:
attach handler for datagrid's MouseLeftButtonDown event using AddHandler dataGrid.AddHandler(UIElement.MouseLeftButtonDownEvent, new MouseButtonEventHandler(DataGrid_MouseLeftButtonDown), true);
, save SelectedIndex in private variable
private int prevSelectedIndex;
void DataGrid_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (prevSelectedIndex != -1 && prevSelectedIndex == dataGrid.SelectedIndex)
{
dataGrid.SelectedIndex = -1;
}
prevSelectedIndex = dataGrid.SelectedIndex;
}
if you want reuse this logic you can create Behavior for DataGrid type
Add System.Windows.Interactivity assembly reference, add class DataGridSecondClickUnselectBehavior
public class DataGridSecondClickUnselectBehavior : Behavior<DataGrid>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.AddHandler(UIElement.MouseLeftButtonDownEvent, new MouseButtonEventHandler(AssociatedObject_MouseLeftButtonDown), true);
}
private int prevSelectedIndex;
void AssociatedObject_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (prevSelectedIndex != -1 && prevSelectedIndex == AssociatedObject.SelectedIndex)
{
AssociatedObject.SelectedIndex = -1;
}
prevSelectedIndex = AssociatedObject.SelectedIndex;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.RemoveHandler(UIElement.MouseLeftButtonDownEvent, new MouseButtonEventHandler(AssociatedObject_MouseLeftButtonDown));
}
}
Now after you compile solution in blend you can add this behavior simply Drag'n'drop from Assets->Behaviors to DataGrid control

How to view drag & drop element in WPF?

I have a ListBox and a DockPanel. List box contains items that are supposed to be dragged onto the dock panel. I've implemented that by following this link.
There are a couple of things I do not understand though:
While dragging, all I see is a cursor. I'd like to literary see the list item I am
dragging to move around with my cursor. How do I do that?
Is the DragDropEffect property only for the different cursor design or it has a
higher purpose? :)
How do I make list item disappear from the ListBox once it is dropped onto the
DockPanel?
I'd like to enforce some animation on the items that I drag, like glow once it is
dropped. Which trigger/setter should I use for that?
Here's my code for basic dragging and dropping:
Code-behind for the ListBox part
private Point startPosition;
private void ListBox_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
startPosition = e.GetPosition(null);
}
private void ListBox_PreviewMouseMove(object sender, MouseEventArgs e)
{
Point currentPosition;
Vector offset;
ListBox listBox;
ListBoxItem item;
Match match;
DataObject dragData;
currentPosition = e.GetPosition(null);
offset = startPosition - currentPosition;
if (e.LeftButton == MouseButtonState.Pressed &&
(Math.Abs(offset.X) > SystemParameters.MinimumHorizontalDragDistance ||
Math.Abs(offset.Y) > SystemParameters.MinimumVerticalDragDistance))
{
// Get the data binded to ListBoxItem object, which is "match"
listBox = sender as ListBox;
item = FindAnchestor<ListBoxItem>((DependencyObject)e.OriginalSource);
match = (Match)listBox.ItemContainerGenerator.ItemFromContainer(item);
dragData = new DataObject("match", match);
DragDrop.DoDragDrop(item, dragData, DragDropEffects.Move);
}
}
Code-behind for the DockPanel part
private void DockPanel_DragEnter(object sender, DragEventArgs e)
{
if (!e.Data.GetDataPresent("match") ||
sender == e.Source)
{
e.Effects = DragDropEffects.None;
}
}
private void DockPanel_Drop(object sender, DragEventArgs e)
{
Match match;
DockPanel matchSlot;
ContentPresenter contentPresenter;
Binding binding;
if (e.Data.GetDataPresent("match"))
{
match = e.Data.GetData("match") as Match;
matchSlot = sender as DockPanel;
contentPresenter = new ContentPresenter();
contentPresenter.ContentTemplate = this.FindResource("MatchTemplate") as DataTemplate;
binding = new Binding();
binding.Source = match;
contentPresenter.SetBinding(ContentPresenter.ContentProperty, binding);
matchSlot.Children.Clear();
matchSlot.Children.Add(contentPresenter);
}
}
Thanks for all the help.
Ok, after a while I found some answers and discovered a few things on my own.
As for the DragDropEffect enum, it should be used for two reasons:
To distinguish if the item is moved or copied in the code. It serves like a flag and should be used most commonly like this:
if (e.DragDropEffect == DragDropEffect.Move)
{
...
}
else ...
To decorate the mouse cursor based on the enum value. This way it tells the user if he or she is moving or copying the item.
As for the drag and drop visualization here's a link to the post containing the reference which is an excellent starting point for drag and drop to build on: WPF Drag & Drop: How to literally drag an element?

Close current UserControl

I have a Window1.xaml main Window; and after some event, I display a UserControl EditFile.xaml.
The code behind is:
public static int whichSelected = -1;
private void button1_Click(object sender, RoutedEventArgs e)
{
//searchEditPanel.Children.Clear();
whichSelected = listViewFiles.SelectedIndex;
searchEditPanel.Children.Add(_EditFileControle); //this is Grid
}
And now, how can I close the opened/added UserControl from its content by clicking a Cancel button or something like that?
Window.GetWindow(this).Close();
You don't need to use a new variable, you can use it directly.
In your button click handler try :
Window parentWindow = (Window)this.Parent;
parentWindow.Close();
You could set the Visibility property of the control you want to "close" to Collapsed.
This way it will not be displayed anymore but will still be present in the visual tree if you need to reuse it later.
Have you tried this?
searchEditPanel.Children.Remove(_EditFileControle);
Another Suggestion:
Maybe this helps: http://sachabarber.net/?p=162
if it doesn't: Add a property to your UserControl:
public UserControl ParentControl {get;set;}
Now modify your code:
private void button1_Click(object sender, RoutedEventArgs e)
{
//searchEditPanel.Children.Clear();
whichSelected = listViewFiles.SelectedIndex;
_EditFileControle.ParentControl = this;
searchEditPanel.Children.Add(_EditFileControle); //this is Grid
}
Now you should be able to do this:
// Somewhere in your UserControl
if (this.ParentControl != null)
this.ParentControl.Children.Remove(this);
private void Button_Click(object sender, RoutedEventArgs e)
{
(this.Parent as searchEditPanel).Children.Remove(this);
}

WPF: How to programmatically remove focus from a TextBox

I want to add a simple (at least I thought it was) behaviour to my WPF TextBox.
When the user presses Escape I want the TextBox he is editing to have the text it had when the user started editing, AND I want to remove the focus from the TextBox.
I don't have any problem setting the text for the value it had in the beginning of the edit.
The problem is to remove the focus of the element. I don't want to move the focus to any other component, I just want the TextBox to lose focus. Will I have to have an invisible element to set the focus so my TextBox can lose focus?
in .NET Framework 4 just Keyboard.ClearFocus();
The code I have been using :
// Move to a parent that can take focus
FrameworkElement parent = (FrameworkElement)textBox.Parent;
while (parent != null && parent is IInputElement && !((IInputElement)parent).Focusable)
{
parent = (FrameworkElement)parent.Parent;
}
DependencyObject scope = FocusManager.GetFocusScope(textBox);
FocusManager.SetFocusedElement(scope, parent as IInputElement);
Since none of the above answers worked for me and the accepted answer does work only for a keyboard focus, I came to the following approach:
// Kill logical focus
FocusManager.SetFocusedElement(FocusManager.GetFocusScope(textBox), null);
// Kill keyboard focus
Keyboard.ClearFocus();
Kills both, logical as well as the keyboard focus.
A bit late to the party, but it was helpful to me so here it goes.
Since .Net 3.0, FrameworkElement has a MoveFocus function which did the trick for me.
You can set the focus to a focusable ancestor. This code will work even if the textbox is inside a template with no focusable ancestors inside that same template:
DependencyObject ancestor = textbox.Parent;
while (ancestor != null)
{
var element = ancestor as UIElement;
if (element != null && element.Focusable)
{
element.Focus();
break;
}
ancestor = VisualTreeHelper.GetParent(ancestor);
}
AFAIK, it is not possible to completely remove the focus. Something in your Window will always have the focus.
For me, it's quite tricky, especially when using with LostFocus binding.
However, my workaround is to add an empty label and focus on it.
<Label Name="ResetFocusArea" Focusable="True" FocusVisualStyle="{x:Null}" />
...
OnKeyDown(object sender, RoutedEventArgs e)
{
//if is Esc
ResetFocusArea.Focus();
}
Using LPL's answer worked for me, but it would also make me unable to select any options in dropdown menues. To combat this, I added a check to see if the focused element was a textbox.
Doing the same check for when pressing enter, my final code looked like this:
public Menu()
{
InitializeComponent();
this.PreviewMouseDown += PreviewMouseDownEventHandler;
this.KeyDown += WindowKeyDownHandler;
}
void ClearFocus()
{
UIElement elementWithFocus = Keyboard.FocusedElement as UIElement;
if (elementWithFocus is System.Windows.Controls.TextBox tb)
{
if (Keyboard.FocusedElement != null)
{
Keyboard.FocusedElement.RaiseEvent(new RoutedEventArgs(UIElement.LostFocusEvent));
Keyboard.ClearFocus();
}
}
}
private void PreviewMouseDownEventHandler(object sender, MouseButtonEventArgs e)
{
ClearFocus();
}
private void WindowKeyDownHandler(object sender, System.Windows.Input.KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
ClearFocus();
}
}
With this, I didn't need to add a focuslost to every textbox, and it can easily extend to other elements without breaking compatability with other parts of the program.
In Windows Phone Development, I just did Focus() or this.Focus() in the PhoneApplicationPage and it worked like a charm.
My answer does not adress the above question directly, however, I feel that the wording of it has caused it to become "The Question" about programmatically getting rid of focus. A common scenario where this is needed is for the user to be able to clear focus upon left-clicking the background of a root control, like window.
So, to achieve this, you can create an Attached Behavior that will switch focus to a dynamically created control (in my case, an empty label). It is preferrable to use this behavior on the highest-level elements like windows, as it iterates through it's children to find a panel it can add a dummy label to.
public class LoseFocusOnLeftClick : Behavior<FrameworkElement>
{
private readonly MouseBinding _leftClick;
private readonly Label _emptyControl = new Label() { Focusable = true, HorizontalAlignment = HorizontalAlignment.Left, VerticalAlignment = VerticalAlignment.Top };
public LoseFocusOnLeftClick()
{
_leftClick = new MouseBinding(new RelayCommand(LoseFocus), new MouseGesture(MouseAction.LeftClick));
}
protected override void OnAttached()
{
AssociatedObject.InputBindings.Add(_leftClick);
AssociatedObject.Loaded += AssociatedObject_Loaded;
}
protected override void OnDetaching()
{
AssociatedObject.InputBindings.Remove(_leftClick);
AssociatedObject.Loaded -= AssociatedObject_Loaded;
}
private void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
{
AssociatedObject.Loaded -= AssociatedObject_Loaded;
AttachEmptyControl();
}
private void AttachEmptyControl()
{
DependencyObject currentElement = AssociatedObject;
while (!(currentElement is Panel))
{
currentElement = VisualTreeHelper.GetChild(currentElement, 0);
}
((Panel)currentElement).Children.Add(_emptyControl);
}
private void LoseFocus()
{
_emptyControl.Focus();
}
}
If you want to remove focus from a certain TextBox, just add this line..
textBox.Focusable = false;

How to draw connecting lines between two controls on a grid WPF

I am creating controls (say button) on a grid. I want to create a connecting line between controls.
Say you you do mousedown on one button and release mouse over another button. This should draw a line between these two buttons.
Can some one help me or give me some ideas on how to do this?
Thanks in advance!
I'm doing something similar; here's a quick summary of what I did:
Drag & Drop
For handling the drag-and-drop between controls there's quite a bit of literature on the web (just search WPF drag-and-drop). The default drag-and-drop implementation is overly complex, IMO, and we ended up using some attached DPs to make it easier (similar to these). Basically, you want a drag method that looks something like this:
private void onMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
UIElement element = sender as UIElement;
if (element == null)
return;
DragDrop.DoDragDrop(element, new DataObject(this), DragDropEffects.Move);
}
On the target, set AllowDrop to true, then add an event to Drop:
private void onDrop(object sender, DragEventArgs args)
{
FrameworkElement elem = sender as FrameworkElement;
if (null == elem)
return;
IDataObject data = args.Data;
if (!data.GetDataPresent(typeof(GraphNode))
return;
GraphNode node = data.GetData(typeof(GraphNode)) as GraphNode;
if(null == node)
return;
// ----- Actually do your stuff here -----
}
Drawing the Line
Now for the tricky part! Each control exposes an AnchorPoint DependencyProperty. When the LayoutUpdated event is raised (i.e. when the control moves/resizes/etc), the control recalculates its AnchorPoint. When a connecting line is added, it binds to the DependencyProperties of both the source and destination's AnchorPoints. [EDIT: As Ray Burns pointed out in the comments the Canvas and grid just need to be in the same place; they don't need to be int the same hierarchy (though they may be)]
For updating the position DP:
private void onLayoutUpdated(object sender, EventArgs e)
{
Size size = RenderSize;
Point ofs = new Point(size.Width / 2, isInput ? 0 : size.Height);
AnchorPoint = TransformToVisual(node.canvas).Transform(ofs);
}
For creating the line class (can be done in XAML, too):
public sealed class GraphEdge : UserControl
{
public static readonly DependencyProperty SourceProperty = DependencyProperty.Register("Source", typeof(Point), typeof(GraphEdge), new FrameworkPropertyMetadata(default(Point)));
public Point Source { get { return (Point) this.GetValue(SourceProperty); } set { this.SetValue(SourceProperty, value); } }
public static readonly DependencyProperty DestinationProperty = DependencyProperty.Register("Destination", typeof(Point), typeof(GraphEdge), new FrameworkPropertyMetadata(default(Point)));
public Point Destination { get { return (Point) this.GetValue(DestinationProperty); } set { this.SetValue(DestinationProperty, value); } }
public GraphEdge()
{
LineSegment segment = new LineSegment(default(Point), true);
PathFigure figure = new PathFigure(default(Point), new[] { segment }, false);
PathGeometry geometry = new PathGeometry(new[] { figure });
BindingBase sourceBinding = new Binding {Source = this, Path = new PropertyPath(SourceProperty)};
BindingBase destinationBinding = new Binding { Source = this, Path = new PropertyPath(DestinationProperty) };
BindingOperations.SetBinding(figure, PathFigure.StartPointProperty, sourceBinding);
BindingOperations.SetBinding(segment, LineSegment.PointProperty, destinationBinding);
Content = new Path
{
Data = geometry,
StrokeThickness = 5,
Stroke = Brushes.White,
MinWidth = 1,
MinHeight = 1
};
}
}
If you want to get a lot fancier, you can use a MultiValueBinding on source and destination and add a converter which creates the PathGeometry. Here's an example from GraphSharp. Using this method, you could add arrows to the end of the line, use Bezier curves to make it look more natural, route the line around other controls (though this could be harder than it sounds), etc., etc.
See also
http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/dd246675-bc4e-4d1f-8c04-0571ea51267b
http://www.codeproject.com/KB/WPF/WPFDiagramDesigner_Part1.aspx
http://www.codeproject.com/KB/WPF/WPFDiagramDesigner_Part2.aspx
http://www.codeproject.com/KB/WPF/WPFDiagramDesigner_Part3.aspx
http://www.codeproject.com/KB/WPF/WPFDiagramDesigner_Part4.aspx
http://www.syncfusion.com/products/user-interface-edition/wpf/diagram
http://www.mindscape.co.nz/products/wpfflowdiagrams/

Resources