Convert Code behind MouseButtonEventArgs into MVVM - c#

Im still new to MVVM but im trying to learn it.
I need to resize the Drawn rectangle into my canvas. But i want to do it in MVVM way.
I found this post and its working and it suite my needs but its not MVVM
https://denisvuyka.wordpress.com/2007/10/15/wpf-simple-adorner-usage-with-drag-and-resize-operations/
I am trying to convert this code into MVVM
// Handler for element selection on the canvas providing resizing adorner
void myCanvas_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
// Remove selection on clicking anywhere the window
if (selected)
{
selected = false;
if (selectedElement != null)
{
// Remove the adorner from the selected element
aLayer.Remove(aLayer.GetAdorners(selectedElement)[0]);
selectedElement = null;
}
}
// If any element except canvas is clicked,
// assign the selected element and add the adorner
if (e.Source != myCanvas)
{
_isDown = true;
_startPoint = e.GetPosition(myCanvas);
selectedElement = e.Source as UIElement;
_originalLeft = Canvas.GetLeft(selectedElement);
_originalTop = Canvas.GetTop(selectedElement);
aLayer = AdornerLayer.GetAdornerLayer(selectedElement);
aLayer.Add(new ResizingAdorner(selectedElement));
selected = true;
e.Handled = true;
}
}
I am using Devexpress MVVM
and this is my xaml code
<Canvas Name="myCanvas" Background="Gray">
<dxmvvm:Interaction.Behaviors>
<dxmvvm:EventToCommand Command="{Binding OnSelectedCommand , Source={x:Static vm:Vm.instance}}"
EventName="MouseDown" CommandParameter="{Binding ElementName=myCanvas}"
PassEventArgsToCommand="True">
</dxmvvm:EventToCommand>
</dxmvvm:Interaction.Behaviors>
</Canvas >
i am trying to pass the canvas into my view model.
this is the code for my ViewModel
public DelegateCommand<Canvas> OnSelectedCommand { get; private set; }
public Vm()
{
OnSelectedCommand = new DelegateCommand<Canvas>(OnSelectedEvent, true);
}
Canvas c = new Canvas();
private void OnSelectedEvent(object e)
{
if (e == null) return;
c = e as Canvas;
// Remove selection on clicking anywhere the window
if (selected)
{
selected = false;
if (selectedElement != null)
{
// Remove the adorner from the selected element
aLayer.Remove(aLayer.GetAdorners(selectedElement)[0]);
selectedElement = null;
}
}
// If any element except canvas is clicked,
// assign the selected element and add the adorner
if (e.Source != myCanvas)
{
_isDown = true;
_startPoint = e.GetPosition(myCanvas);
selectedElement = e.Source as UIElement;
_originalLeft = Canvas.GetLeft(selectedElement);
_originalTop = Canvas.GetTop(selectedElement);
aLayer = AdornerLayer.GetAdornerLayer(selectedElement);
aLayer.Add(new ResizingAdorner(selectedElement));
selected = true;
e.Handled = true;
}
}
currently my code doesn't work. In the code behind i have the MouseButtonEventArgs which is needed in the method.
the problem i am facing now is how do it access the MouseButtonEventArgs?
Without it. I can't use the sample code from the blog

You can use Behavior to implement your goal.
Firstly, add the following two assemblies reference to your project:
System.Windows.Interactivity.dll
Microsoft.Expression.Interactions.dll
Add the following xaml namespace:
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
Then add behaviors for the Canvas control like this:
<Canvas>
<i:Interaction.Triggers>
<i:EventTrigger EventName="PreviewMouseLeftButtonDown">
<ei:CallMethodAction MethodName="OnLeftButtonClicked" TargetObject="{Binding}" />
</i:EventTrigger>
</i:Interaction.Triggers>
...
</Canvas>
EventTrigger is a trigger behavior that will listen for a specific event raised up. CallMethodAction is an action that will invoke a method which is specified by TargetObject and MethodName properties. Here, the value of TargetObject is {Binding}, it means the target object is your view model, so it will invoke a method with such a name from your view model.
Then, in your view model, add the following method:
public void OnLeftButtonClicked(object s,MouseButtonEventArgs e)
{
}
Notice:
The modifier should be public;
The method name should be identical to the MethodName property of CallMethodAction;

Related

Get names of control in code behind when clicked, wpf

I have a usercontrol and I want to get the names of the control user clicks on this usercontrol.
The Usercontrol xaml is given below:
<Grid Name="Grid1">
<ListView Name ="ListView1">
<listbox.resources>
<Datatemplate>
<Togglebutton Name="Button1">
</Togglebutton>
</Datatemplate>
</listbox.resources>
</Listview>
<Border Name="Border1">
<ContentControl>
</ContentControl>
</Border>
</Grid>
Now in my code behind, I wrote:
this.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(mouseclicked);
private void mouseclicked(object sender, MouseButtonEventArgs e)
{
var source = e?.OriginalSource as FrameworkElement;
if (source != null)
{
var parent = source.Parent as FrameworkElement;
string name = string.Empty;
if (parent != null)
{
name = parent.Name;
}
else
{
name = source.Name;
}
//If clicked outside the usercontrol, then usercontrol should close down
if (!(name.Contains("Border1") || name.Contains("Grid1"))) //very strange values sometimes null or empty
{
//then do something
}
}
}
How can I get the names of the controls used in my Usercontrol in codebehind?
You have gave the grid, border and listview a name in XAML. This you should access in codebehind and check in the mouseclicked event, which element is mouse over.
private void mouseclicked(object sender, MouseButtonEventArgs e)
{
if (ListView1.IsMouseOver && Grid1.IsMouseOver)
{
MessageBox.Show("List view clicked");
}
if (Border1.IsMouseOver && Grid1.IsMouseOver)
{
MessageBox.Show("Border1 clicked");
}
if (Grid1.IsMouseOver)
{
MessageBox.Show("Grid1 clicked");
}
}
This would may be not the best, but could work to get the names of controls the user has clicked.

Why ListView doesn't update in MVVM [duplicate]

This question already has answers here:
WPF ListView: Changing ItemsSource does not change ListView
(7 answers)
Closed 6 years ago.
I have a little problem and I don't understand where it comes from, I suppose when I will get the answer I will probably said the famous " aaaaaahhhhh yessss ! of course !" so here his my problem : I try to update a listView in mvvm with a drag and drop, with the breakpoints and stuff I can see that the List<string> that goes into the listView is updated and has a new item inside, the element that I pass to the listView Items, but the view doesn't update and the new Item doesn't appear. Here is my code :
private List<string> _listViewItems;
public List<string> listViewItems
{
get
{
return _listViewItems;
}
set
{
_listViewItems = value;
OnPropertyChanged("listViewItems");
}
}
public ICommand MouseMove { get; set; }
//in constructor
MouseMove = new BaseCommand(GoMouseMove);
private void GoMouseMove(object obj)
{
MouseEventArgs e = (MouseEventArgs)obj;
try
{
if (e.LeftButton == MouseButtonState.Pressed)
{
draggedItem = (TreeViewItem) SelectedItem;
if (draggedItem != null)
{
DragDropEffects finalDropEffect = DragDrop.DoDragDrop(SelectedItem, SelectedItem, DragDropEffects.Move);
//Checking target is not null and item is dragging(moving)
if ((finalDropEffect == DragDropEffects.Move))
{
CopyItem(draggedItem, _target);
_target = null;
draggedItem = null;
}
}
}
}
catch (Exception)
{
}
}
private void CopyItem(TreeViewItem _sourceItem, ListViewItem _targetItem)
{
//Asking user wether he want to drop the dragged TreeViewItem here or not
if (MessageBox.Show("Would you like to drop " + _sourceItem.Header.ToString(), "", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
try
{
List<string> items = listViewItems;
items.Add(_sourceItem.Header.ToString());
listViewItems = items;
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
}
}
When i debug i got that :
<ListView Name="listview1"
Grid.ColumnSpan="2"
Width="auto"
Height="auto"
Grid.Row="1"
AllowDrop="True"
ItemsSource="{Binding listViewItems, Mode=TwoWay}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Drop" >
<cmd:EventToCommand Command="{Binding Drop}"
PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ListView>
So we can see that algo.pdf is added, but the view doesn't update. What am I missing ?!
Thank you very much !
A List<> has no way to inform WPF that an item has been added, and therefore WPF can not update the UI to display any added items.
Try using an ObservableCollection<> which will send notificiation events to WPF when you add / remove items.
Also, remember that the items inside your collection should also implement INotifyPropertyChanged if you want WPF to update when properties inside those objects change.

How to prevent listbox items being dragged if mouse down is on certain controls

I would like some advice on drag and drop operations of listbox items. Each of my items has a ComboBox, TextBox, CheckBox and Button, as shown below.
I use my drag/drop to reorder these and it is almost working correctly.
The main issue is that a drag operation on an item is occurring when a PreviewMouseLeftButtonDown and PreviewMouseMove event happens on one of the controls noted above.
My question is given the code below what's good way I can prevent this drag occuring when one of the controls is clicked on?
XAML:
<DataTemplate DataType="{x:Type helpers:Filter}">
<Border>
<Border>
<Grid>
<ComboBox />
<TextBox />
<CheckBox />
<Button />
</Grid>
</Border>
</Border>
</DataTemplate>
<ListBox x:Name="FilterList"
ItemsSource="{Binding Filters}"
helpers:DragDropHelper.IsDragSource="true"
helpers:DragDropHelper.IsDropTarget="true" />
C#:
public static readonly DependencyProperty IsDragSourceProperty =
DependencyProperty.RegisterAttached("IsDragSource", typeof(bool), typeof(DragDropHelper), new UIPropertyMetadata(false, IsDragSourceChanged));
private void DragSource_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
this.sourceItemsControl = (ItemsControl)sender;
Visual visual = e.OriginalSource as Visual;
this.topWindow = Window.GetWindow(this.sourceItemsControl);
this.initialMousePosition = e.GetPosition(this.topWindow);
this.sourceItemContainer = sourceItemsControl.ContainerFromElement(visual) as FrameworkElement;
if (this.sourceItemContainer != null)
{
this.draggedData = this.sourceItemContainer.DataContext;
}
}
// Drag = mouse down + move by a certain amount
private void DragSource_PreviewMouseMove(object sender, MouseEventArgs e)
{
if (this.draggedData != null)
{
// Only drag when user moved the mouse by a reasonable amount.
if (Utilities.IsMovementBigEnough(this.initialMousePosition, e.GetPosition(this.topWindow)))
{
this.initialMouseOffset = this.initialMousePosition - this.sourceItemContainer.TranslatePoint(new Point(0, 0), this.topWindow);
DataObject data = new DataObject(this.format.Name, this.draggedData);
// Adding events to the window to make sure dragged adorner comes up when mouse is not over a drop target.
bool previousAllowDrop = this.topWindow.AllowDrop;
this.topWindow.AllowDrop = true;
this.topWindow.DragEnter += TopWindow_DragEnter;
this.topWindow.DragOver += TopWindow_DragOver;
this.topWindow.DragLeave += TopWindow_DragLeave;
DragDropEffects effects = DragDrop.DoDragDrop((DependencyObject)sender, data, DragDropEffects.Move);
RemoveDraggedAdorner();
this.topWindow.AllowDrop = previousAllowDrop;
this.topWindow.DragEnter -= TopWindow_DragEnter;
this.topWindow.DragOver -= TopWindow_DragOver;
this.topWindow.DragLeave -= TopWindow_DragLeave;
this.draggedData = null;
}
}
}
I tend to initialise my Drag and Drop properties in a PreviewMouseDown event handler, but it's unwise to perform any other Drag and Drop operations in that handler, because the user might not be dragging... they might just have clicked.
Instead, it's better to handle the PreviewMouseMove event to initiate a Drag and Drop operation. Here is a simplified example:
private void DragSourcePreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
isMouseDown = true;
}
private void DragSourcePreviewMouseMove(object sender, MouseEventArgs e)
{
if (isMouseDown && IsConfirmedDrag(e.GetPosition(sender as ListBox)))
{
isMouseDown = false;
...
DragDrop.DoDragDrop(dragSource, data, DragDropEffects);
}
}
It is in the PreviewMouseMove event handler that you could check which UI element has been clicked on and determine whether you start the Drag and Drop operation or not. Try something like this:
private void DragSourcePreviewMouseMove(object sender, MouseEventArgs e)
{
ListBox dragSourceControl = (ListBox)sender;
HitTestResult result = VisualTreeHelper.HitTest(dragSourceControl,
Mouse.GetPosition(dragSourceControl));
UIElement draggedUIElement = result.VisualHit.GetParentOfType<ListBoxItem>();
bool isViable = AddYourViabilityConditionHere(draggedUIElement);
if (isMouseDown && IsConfirmedDrag(e.GetPosition(sender as ListBox)) && isViable)
{
isMouseDown = false;
...
DragDrop.DoDragDrop(dragSource, data, DragDropEffects);
}
}

c# - how to handle with null selectedItem?

i have an issue with selectedItem of a listbox. When I select an item of the listbox, a popup would be displayed where you click the add button to select an image (it contains a value of selectedItem) which is working fine. But after clicking the add button to select the image, then you realise the image is wrong, so you click the add button again to select another image, it started problem because selectedItem is null. How to handle it? How to stay the value of selectedItem? Your given code much appreciated.
if (lstDinner.SelectedItem != null)
{
output = _imageInserter.InsertImage(imageName, lstDinner.SelectedItem.ToString());
PopupToysImage.IsOpen = true;
strDinner.DinnersDetails = lstDinner.SelectedItem.ToString()
}
else
{
// strDinner.DinnersDetails = null that cause a problem.
output = _imageInserter.InsertImage(imageName, strDinner.DinnersDetails);
PopupDinnerImage.IsOpen = true;
}
UPDATE HERE:
WPF:
<ListBox Style="{DynamicResource ListBoxStyle1}" DisplayMemberPath="Dinner" BorderBrush="#FFF0F0F0" x:Name="lstDinner" FontSize="20" HorizontalAlignment="Left" Margin="0,110,0,72.667" Width="436" SelectionMode="Extended" PreviewMouseLeftButtonDown="MouseDownHandler" ScrollViewer.CanContentScroll="True" UseLayoutRounding="False" KeyDown="lstDinner_KeyDown" MouseDoubleClick="lstDinner_MouseDoubleClick" >
events in C#:
private void MouseDownHandler(object sender, MouseButtonEventArgs e)
{
var parent = (ListBox)sender;
_dragSource = parent;
var data = GetObjectDataFromPoint(parent, e.GetPosition(parent));
if (e.ChangedButton == MouseButton.Left && e.ClickCount == 1)
{
if (data != null)
DragDrop.DoDragDrop(parent, data, DragDropEffects.Move);
}
}
private void lstDinner_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Delete)
{
RemoveItemsFromDatabase();
}
}
private void lstDinner_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
_dinnerImage = new DinnerImageExtractor();
BitmapImage getImage = new BitmapImage();
if (lstDinner.SelectedItem != null)
{
getImage = _dinnerImage.GetDinnerImages(lstDinner.SelectedItem.ToString());
if (getImage != null)
{
DinnerImagePopup.Source = getImage;
}
else
{
DinnerImagePopup.Source = new BitmapImage(new Uri("/DinnerApplicationWPF;component/Menu/Images/noImage-icon-pink.png", UriKind.Relative));
}
PopupDinnerImage.IsOpen = true;
// PopupInstrcution.IsOpen = false;
}
}
I would suggest something like this
if ( lstDinner.SelectedItem == null)
{
output = _imageInserter.InsertImage(imageName, lstToys.SelectedItem.ToString());
PopupToysImage.IsOpen = true;
lstDinner.Databind();
}
Note: This may not work as I dont have your actual code. I have added DataBind() in the if statement, if the selected item was null. It should refresh the list.
Best thing is to use two different Listbox item templates for selected and unselected items. So without displaying popup, you can add button into the selected item template.
Are you disabling the ListBox while you select the image?
If so I believe by simply disabling the ListBox the SelectedItem will be set to null.
EDIT:
I imagine you want your event handlers (like the mouse double click) to happen when an item in your list is double clicked, not when the ListBox is double clicked. You need to change your XAML to this:
<ListBox Style="{DynamicResource ListBoxStyle1}" DisplayMemberPath="Dinner" BorderBrush="#FFF0F0F0" x:Name="lstDinner" FontSize="20" HorizontalAlignment="Left" Margin="0,110,0,72.667" Width="436" SelectionMode="Extended" PreviewMouseLeftButtonDown="MouseDownHandler" ScrollViewer.CanContentScroll="True" UseLayoutRounding="False" KeyDown="lstDinner_KeyDown">
<ListBox.Resources>
<Style TargetType="ListBoxItem">
<EventSetter Event="MouseDoubleClick" Handler="lstDinner_MouseDoubleClick" />
</Style>
</ListBox.Resources>
</ListBox>
My selected item does not come up null when I run this code.

How can I make a RichTextBox scroll to the end when I add a new line?

I have several read only RichTextBox's that are used for logging output. Since they're read only they don't seem to automatically scroll when the text is updated. I can use the TextChanged event to force a scroll to end, but is there not simply a way to set a property or something in the XAML so that scrolling happens like normal?
I had googled for your problem and found this post.
In the section "Programming the RichTextBox" author had described about getting the behavior what you had been expecting.
Please check and let me know if it is of any use.
I tried to reproduce your problem and came up with the following solution
<Window x:Class="CheckRichTextBox.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="170" Width="300">
<StackPanel>
<RichTextBox Height="100" Name="richTextBox1" IsReadOnly="True" VerticalScrollBarVisibility="Visible"/>
<Button Name="btnAdd" Content="Click me to add text" VerticalAlignment="Bottom" Click="BtnAddClick" />
</StackPanel>
</Window>
The code behind for the same is as below:
using System.Windows;
namespace CheckRichTextBox
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void BtnAddClick(object sender, RoutedEventArgs e)
{
richTextBox1.AppendText("You had Clicked the button for adding text\n");
richTextBox1.ScrollToEnd();
}
}
}
This solves the problem of autoscroll, please check it and let me know if it is of any help.
I solved this problem using an Interactivity trigger and a very simple action.
The action looks like this:
public class ScrollToBottomAction : TriggerAction<RichTextBox>
{
protected override void Invoke(object parameter)
{
AssociatedObject.ScrollToEnd();
}
}
Then in my XAML I have this:
<RichTextBox IsReadOnly="True" VerticalScrollBarVisibility="Auto">
<i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<interactivity:ScrollToBottomAction/>
</i:EventTrigger>
</i:Interaction.Triggers>
</RichTextBox>
I came up with the following solution for wpf richtextbox autoscroll
public partial class MainWindow
{
private bool AutoScroll = true;
public MainWindow()
{
InitializeComponent();
yourRichTextBox.Loaded += (s, e) =>
{
var scrollViewer = VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(yourRichTextBox, 0), 0) as ScrollViewer;
scrollViewer.ScrollChanged += (scroller, eScroller) => ScrollViewer_ScrollChanged(scroller, eScroller);
};
}
private void ScrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
// User scroll event : set or unset autoscroll mode
if (e.Source as ScrollViewer != null && e.ExtentHeightChange == 0)
{ // Content unchanged : user scroll event
if ((e.Source as ScrollViewer).VerticalOffset == (e.Source as ScrollViewer).ScrollableHeight)
{ // Scroll bar is in bottom
// Set autoscroll mode
AutoScroll = true;
}
else
{ // Scroll bar isn't in bottom
// Unset autoscroll mode
AutoScroll = false;
}
}
// Content scroll event : autoscroll eventually
if (AutoScroll && e.ExtentHeightChange != 0 && e.Source as ScrollViewer != null)
{ // Content changed and autoscroll mode set
// Autoscroll
(e.Source as ScrollViewer).ScrollToVerticalOffset((e.Source as ScrollViewer).ExtentHeight);
}
}
}
This is how to do it in C#
Put the RichTextBox in a ScrollViewer like this:
scrollViewer.HorizontalScrollBarVisibility = ScrollBarVisibility.Auto;
scrollViewer.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
scrollViewer.Content = rTextBox;
Then add the scrollViewer to the Grid or whatever you are using.
RootGrid.Children.Add(scrollViewer);
(This is in C# but could all be done in XAML as well.)
Then use C# code like this to make it scroll to the bottom when you add text:
rTextBox.AppendText(str);
scrollViewer.ScrollToEnd();
Hope that helps.
RichTextBox.AppendText("String")
RichTextBox.ScrollToCaret()
When I was adding to RichTextBox.text, ScrollToCaret() does not work.
RichTextBox.text = RichTextBox.text + "String"
RichTextBox.ScrollToCaret()

Categories