Is it possible to Animate a Binding? - c#

Is it possible to Animate a picture which i get through binding? As i am new to C# and WPF i am trying to use the Features like binding. I want the old picture fading out (Opacity probaply, but is this an option with Binding?) and the new one fading in. Or is it better to handle everything in the code behind, where i get the image from the folder and binding it afterwards.
<UserControl x:Class="Screensaver.ScreensaverControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Screensaver"
mc:Ignorable="d" DataContext="{Binding RelativeSource={RelativeSource Self}}"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Image Visibility="Visible" Source="{Binding DisplayedImagePath}" Name="Bild" Stretch="Uniform" />
</Grid>
</UserControl>
using System;
using System.IO;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
namespace Screensaver
{
/// <summary>
/// Interaction logic for ScreensaverControl.xaml
/// </summary>
public partial class ScreensaverControl : UserControl
{
private Timer _timer;
public ScreensaverControl()
{
InitializeComponent();
this.Loaded += ScreensaverControl_Loaded;
this.Unloaded += ScreensaverControl_Unloaded;
}
private void ScreensaverControl_Loaded(object sender, RoutedEventArgs e)
{
_timer = new Timer(OnTimer, _timer, 10, Timeout.Infinite);
}
private void ScreensaverControl_Unloaded(object sender, RoutedEventArgs e)
{
_timer.Dispose();
_timer = null;
}
private int _index = -1;
private void OnTimer(object state)
{
try
{
var files = Directory.GetFiles(#"C:\Users\mhj\source\repos\Screensaver\Fotos\", "*.png");
if (files.Length > 0)
{
_index++;
if (_index >= files.Length)
_index = 0;
this.Dispatcher.Invoke(() => DisplayedImagePath = files[_index]);
}
}
catch (Exception ex)
{
}
finally
{
if (_timer != null)
_timer.Change(10000, Timeout.Infinite);
}
}
public string DisplayedImagePath
{
get { return (string)GetValue(DisplayedImagePathProperty); }
set { SetValue(DisplayedImagePathProperty, value); }
}
// Using a DependencyProperty as the backing store for DisplayedImagePath. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DisplayedImagePathProperty =
DependencyProperty.Register("DisplayedImagePath", typeof(string), typeof(ScreensaverControl), new PropertyMetadata(null));
}
}

Here is the complete code of a simple screen saver control. It has a dependency property for the image directory. You should add another dependency property for the change interval, and perhaps also a second Image for a blending effect.
XAML:
<UserControl x:Class="ScreenSaverControlTest.ScreenSaverControl" ...>
<Grid>
<Image x:Name="image1"/>
<Image x:Name="image2"/>
</Grid>
</UserControl>
and code behind:
public partial class ScreenSaverControl : UserControl
{
public static readonly DependencyProperty ImageDirectoryProperty =
DependencyProperty.Register(
nameof(ImageDirectory), typeof(string), typeof(ScreenSaverControl),
new PropertyMetadata(ImageDirectoryPropertyChanged));
private readonly DispatcherTimer timer = new DispatcherTimer();
private string[] imagePaths = new string[0];
private int currentImageIndex = -1;
public ScreenSaverControl()
{
InitializeComponent();
timer.Interval = TimeSpan.FromSeconds(10);
timer.Tick += (s, e) => NextImage();
timer.Start();
}
public string ImageDirectory
{
get { return (string)GetValue(ImageDirectoryProperty); }
set { SetValue(ImageDirectoryProperty, value); }
}
private static void ImageDirectoryPropertyChanged(
DependencyObject o, DependencyPropertyChangedEventArgs e)
{
var ssc = (ScreenSaverControl)o;
var directory = (string)e.NewValue;
if (!string.IsNullOrEmpty(directory) && Directory.Exists(directory))
{
ssc.imagePaths = Directory.GetFiles(directory, "*.jpg");
}
else
{
ssc.imagePaths = new string[0];
}
ssc.currentImageIndex = -1;
ssc.NextImage();
}
private void NextImage()
{
if (imagePaths.Length > 0)
{
if (++currentImageIndex >= imagePaths.Length)
{
currentImageIndex = 0;
}
SetImage(new BitmapImage(new Uri(imagePaths[currentImageIndex])));
}
}
private void SetImage(ImageSource imageSource)
{
var fadeOut = new DoubleAnimation(0d, TimeSpan.FromSeconds(1));
var fadeIn = new DoubleAnimation(1d, TimeSpan.FromSeconds(1));
var newImage = image1;
var oldImage = image2;
if (image1.Source != null)
{
newImage = image2;
oldImage = image1;
}
fadeOut.Completed += (s, e) => oldImage.Source = null;
oldImage.BeginAnimation(OpacityProperty, fadeOut);
newImage.BeginAnimation(OpacityProperty, fadeIn);
newImage.Source = imageSource;
}
}
Use it like this:
<local:ScreenSaverControl ImageDirectory="C:\Users\Public\Pictures\Sample Pictures"/>

Related

Usercontrol not showing in designer

I have a custom user control:
<UserControl Name="ddTextBox" x:Class="UserControlsLibrary.DDTextBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignWidth="250" FontFamily="Tahoma" FontSize="14" MinHeight="25" Height="275">
<StackPanel>
<TextBox Name="ControlText" Height="25" HorizontalAlignment="Stretch" VerticalAlignment="Top" Text="{Binding Path=Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<ListBox Name="ControlList" VerticalAlignment="Top" MaxHeight="250" HorizontalAlignment="Stretch" Visibility="Collapsed" ItemsSource="{Binding Path=DList, UpdateSourceTrigger=PropertyChanged}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</UserControl>
With the code behind:
using System;
using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace UserControlsLibrary
{
public partial class DDTextBox : UserControl, INotifyPropertyChanged
{
private string _text;
public string Text { get => _text ?? string.Empty; set { _text = value; OnPropertyChanged(); } }
public DDTextBox()
{
InitializeComponent();
ControlText.DataContext = this;
ControlList.DataContext = this;
ControlText.TextChanged += TextChanged;
ControlText.GotKeyboardFocus += KeyboardFocusChanged;
ControlText.LostKeyboardFocus += KeyboardFocusChanged;
ControlList.SelectionChanged += SelectionChanged;
ControlList.GotKeyboardFocus += KeyboardFocusChanged;
ControlList.LostKeyboardFocus += KeyboardFocusChanged;
IsKeyboardFocusWithinChanged += DDTextBox_IsKeyboardFocusWithinChanged;
}
private void TextChanged(object sender, TextChangedEventArgs e)
{ DItemsSource = ItemsSource.Cast<string>().ToList().FindAll(r => r.IndexOf(( (TextBox)sender ).Text, StringComparison.OrdinalIgnoreCase) >= 0); }
private void SelectionChanged(object sender, SelectionChangedEventArgs e)
{ Text = ( e.AddedItems.Count > 0 ) ? e.AddedItems[0].ToString() : string.Empty; ( (ListBox)sender ).Visibility = Visibility.Collapsed; }
private void KeyboardFocusChanged(object sender, KeyboardFocusChangedEventArgs e)
{
ControlList.Visibility = ( e.NewFocus == ControlList || e.NewFocus == ControlText ) ? Visibility.Visible : Visibility.Collapsed;
if ( e.OldFocus?.GetType() == typeof(ListBox) ) ( (ListBox)e.OldFocus ).SelectedIndex = -1;
}
private void DDTextBox_IsKeyboardFocusWithinChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if ( (bool)e.OldValue == true && (bool)e.NewValue == false )
{ RaiseEvent(new RoutedEventArgs(UserControlLostFocusEvent, this)); }
}
private void ControlLostFocus(object sender, RoutedEventArgs e)
{ if ( !ControlList.IsFocused && !ControlText.IsFocused ) { ControlList.Visibility = Visibility.Collapsed; ControlList.SelectedIndex = -1; } }
public event RoutedEventHandler UserControlLostFocus
{
add { AddHandler(UserControlLostFocusEvent, value); }
remove { RemoveHandler(UserControlLostFocusEvent, value); }
}
public IEnumerable ItemsSource
{
private get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); SetValue(DItemsSourceProperty, value); }
}
/// <summary>
/// Dynamically generated ItemsSource based on Text property. Changes made to this may be lost. List changes should be made to the ItemsSource.
/// </summary>
public IEnumerable DItemsSource
{
private get { return (IEnumerable)GetValue(DItemsSourceProperty); }
set { SetValue(DItemsSourceProperty, value); }
}
public static DependencyProperty DItemsSourceProperty => dItemsSourceProperty;
public static DependencyProperty ItemsSourceProperty => itemsSourceProperty;
public static RoutedEvent UserControlLostFocusEvent => userControlLostFocusEvent;
private static readonly DependencyProperty itemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(DDTextBox), new PropertyMetadata(new PropertyChangedCallback(OnItemsSourcePropertyChanged)));
private static readonly DependencyProperty dItemsSourceProperty = DependencyProperty.Register("DItemsSource", typeof(IEnumerable), typeof(DDTextBox), new PropertyMetadata(new PropertyChangedCallback(OnItemsSourcePropertyChanged)));
private static readonly RoutedEvent userControlLostFocusEvent = EventManager.RegisterRoutedEvent("UserControlLostFocus", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(DDTextBox));
private static void OnItemsSourcePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{ if ( sender is DDTextBox control ) control.OnItemsSourceChanged((IEnumerable)e.NewValue, (IEnumerable)e.OldValue); }
private void OnItemsSourceChanged(IEnumerable newValue, IEnumerable oldValue)
{
if ( oldValue is INotifyCollectionChanged oldValueINotifyCollectionChanged )
{ oldValueINotifyCollectionChanged.CollectionChanged -= new NotifyCollectionChangedEventHandler(NewValueINotifyCollectionChanged_CollectionChanged); }
if ( newValue is INotifyCollectionChanged newValueINotifyCollectionChanged )
{ newValueINotifyCollectionChanged.CollectionChanged += new NotifyCollectionChangedEventHandler(NewValueINotifyCollectionChanged_CollectionChanged); }
}
void NewValueINotifyCollectionChanged_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { }
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string Name = "")
{ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(Name)); }
}
}
Referenced in my main project and added to the main window:
<Window Name="Main" x:Class="POSystem.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:myCtrls="clr-namespace:UserControlsLibrary;assembly=UserControlsLibrary"
Title="Purchase Order" Height="850" Width="1100" Background="#FFD8D0A9">
<Grid Name="TheGrid" Focusable="True" MouseDown="ClearFocus_OnClick" Background="Transparent">
<myCtrls:DDTextBox x:Name="ItemsBox" Width="300" VerticalAlignment="Top" HorizontalAlignment="Left" Panel.ZIndex="1"/>
</Grid>
</Window>
At runtime everything shows up and works as it is supposed too, however, in the window designer of the main page it does not show anything other than an outline indicating that the control is there. This outline can range from a single line at the location (only way all the functionality works proper) or if I add a minimum or just straight height it shows an empty outline of the control. How do I get this to show the (supposed to be) visible at all times textbox within the user control in the main window designer?

Mouse Position with respect to image in WPF using MVVM

I'm trying to conform to the MVVM structure when getting the mouse position when it is over an image in a wpf application. The mouse position should be converted to a pixel location with respect to the image.
I have this working when Image_MouseMove is in ImagePositionView.xaml.cs, but I'm at a bit of a loss (even after trying to read other threads) as to how to achieve this using the MVVM structure.
I have added a reference to MVVMLight in hopes that this will make this task easier, but I have never used to before..
This is what I have so far:
View:
I have added these references based on what I have seen:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:cmd="http://www.galasoft.ch/mvvmlight"
<UserControl x:Class="ImagePixelLocation.View.ImagePositionView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:cmd="http://www.galasoft.ch/mvvmlight"
xmlns:local="clr-namespace:ImagePixelLocation"
mc:Ignorable="d"
d:DesignHeight="600" d:DesignWidth="1000" Background="White">
<Grid>
<Viewbox HorizontalAlignment="Center">
<Grid Name="ColorImage">
<Image x:Name="ImageOnDisplay" Source="{Binding ColourImage}" Stretch="UniformToFill" />
</Grid>
</Viewbox>
</Grid>
</UserControl>
ViewModel:
ViewModelBase exposes INofityPropertyChanged and IDisposable
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using GalaSoft.MvvmLight;
namespace ImagePixelView.ViewModel
{
class ImagePositionViewModel : ViewModelBase
{
private WriteableBitmap colourBitmap = null;
public ImageSource ColourImage
{
get
{
return this.colourBitmap;
}
}
public ManualSelectionViewModel()
{
// Open image to writeablebitmap
string path = #"C:\Some\Path\To\ColorImage.png";
Stream imageStreamSource = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
var decoder = new PngBitmapDecoder(imageStreamSource, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
BitmapSource source = decoder.Frames[0];
int width = source.PixelWidth;
int height = source.PixelHeight;
int stride = source.Format.BitsPerPixel / 8 * width;
byte[] data = new byte[stride * height];
source.CopyPixels(data, stride, 0);
this.colourBitmap = new WriteableBitmap(width, height, 96.0, 96.0, source.Format, null);
this.colourBitmap.WritePixels(new Int32Rect(0, 0, width, height), data, stride, 0);
}
private void Image_MouseMove(object sender, MouseEventArgs e)
{
BitmapSource bitmapImage = (BitmapSource)this.ColourImage;
string xCoord = (e.GetPosition(ImageOnDisplay).X * bitmapImage.PixelWidth / ImageOnDisplay.ActualWidth).ToString();
string yCoord = (e.GetPosition(ImageOnDisplay).Y * bitmapImage.PixelHeight / ImageOnDisplay.ActualHeight).ToString();
System.Diagnostics.Debug.WriteLine("mouse location is X:" + xCoord + ", Y:" + yCoord);
}
}
}
I guess the main thing is how do I get access to the view element ImageOnDisplay from within ImagePositionViewModel.
I do this with a behaviour. First I declare an interface that my view model will implement:
public interface IMouseCaptureProxy
{
event EventHandler Capture;
event EventHandler Release;
void OnMouseDown(object sender, MouseCaptureArgs e);
void OnMouseMove(object sender, MouseCaptureArgs e);
void OnMouseUp(object sender, MouseCaptureArgs e);
}
public class MouseCaptureArgs
{
public double X {get; set;}
public double Y { get; set; }
public bool LeftButton { get; set; }
public bool RightButton { get; set; }
}
And here's a behaviour that uses it:
public class MouseCaptureBehavior : Behavior<FrameworkElement>
{
public static readonly DependencyProperty ProxyProperty = DependencyProperty.RegisterAttached(
"Proxy",
typeof(IMouseCaptureProxy),
typeof(MouseCaptureBehavior),
new PropertyMetadata(null, OnProxyChanged));
public static void SetProxy(DependencyObject source, IMouseCaptureProxy value)
{
source.SetValue(ProxyProperty, value);
}
public static IMouseCaptureProxy GetProxy(DependencyObject source)
{
return (IMouseCaptureProxy)source.GetValue(ProxyProperty);
}
private static void OnProxyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.OldValue is IMouseCaptureProxy)
{
(e.OldValue as IMouseCaptureProxy).Capture -= OnCapture;
(e.OldValue as IMouseCaptureProxy).Release -= OnRelease;
}
if (e.NewValue is IMouseCaptureProxy)
{
(e.NewValue as IMouseCaptureProxy).Capture += OnCapture;
(e.NewValue as IMouseCaptureProxy).Release += OnRelease;
}
}
static void OnCapture(object sender, EventArgs e)
{
var behavior = sender as MouseCaptureBehavior;
if (behavior != null)
behavior.AssociatedObject.CaptureMouse();
}
static void OnRelease(object sender, EventArgs e)
{
var behavior = sender as MouseCaptureBehavior;
if (behavior != null)
behavior.AssociatedObject.ReleaseMouseCapture();
}
protected override void OnAttached()
{
base.OnAttached();
this.AssociatedObject.PreviewMouseDown += OnMouseDown;
this.AssociatedObject.PreviewMouseMove += OnMouseMove;
this.AssociatedObject.PreviewMouseUp += OnMouseUp;
}
protected override void OnDetaching()
{
base.OnDetaching();
this.AssociatedObject.PreviewMouseDown -= OnMouseDown;
this.AssociatedObject.PreviewMouseMove -= OnMouseMove;
this.AssociatedObject.PreviewMouseUp -= OnMouseUp;
}
private void OnMouseDown(object sender, MouseButtonEventArgs e)
{
var proxy = GetProxy(this);
if (proxy != null)
{
var pos = e.GetPosition(this.AssociatedObject);
var args = new MouseCaptureArgs {
X = pos.X,
Y = pos.Y,
LeftButton = (e.LeftButton == MouseButtonState.Pressed),
RightButton = (e.RightButton == MouseButtonState.Pressed)
};
proxy.OnMouseDown(this, args);
}
}
private void OnMouseMove(object sender, MouseEventArgs e)
{
var proxy = GetProxy(this);
if (proxy != null)
{
var pos = e.GetPosition(this.AssociatedObject);
var args = new MouseCaptureArgs {
X = pos.X,
Y = pos.Y,
LeftButton = (e.LeftButton == MouseButtonState.Pressed),
RightButton = (e.RightButton == MouseButtonState.Pressed)
};
proxy.OnMouseMove(this, args);
}
}
private void OnMouseUp(object sender, MouseButtonEventArgs e)
{
var proxy = GetProxy(this);
if (proxy != null)
{
var pos = e.GetPosition(this.AssociatedObject);
var args = new MouseCaptureArgs
{
X = pos.X,
Y = pos.Y,
LeftButton = (e.LeftButton == MouseButtonState.Pressed),
RightButton = (e.RightButton == MouseButtonState.Pressed)
};
proxy.OnMouseUp(this, args);
}
}
}
To use this behaviour you add it to the target UI element and bind to an object that implements the proxy interface. In this case I made the MainViewModel implement the interface so I just bind to that:
<!-- Canvas must have a background, even if it's Transparent -->
<Canvas Background="White" xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity">
<i:Interaction.Behaviors>
<behaviors:MouseCaptureBehavior Proxy="{Binding}" />
</i:Interaction.Behaviors>
The view model now needs to provide mouse handlers which the behaviour will call, it also needs to provide the Capture/Release events which the behaviour will respond to when raised by the view model:
public class MainViewModel : ViewModelBase, IMouseCaptureProxy
{
public event EventHandler Capture;
public event EventHandler Release;
public void OnMouseDown(object sender, MouseCaptureArgs e) {...}
public void OnMouseMove(object sender, MouseCaptureArgs e) {...}
public void OnMouseUp(object sender, MouseCaptureArgs e) {...}
}
UPDATE: It should be self-evident, but just in case not: the sender that you pass in to the Capture and Release events should be the same one you received via the MouseDown/Move/Up handlers. The event args passed to Capture/Receive aren't used and can be null.
Solution for Loading image and display its xy coordinates with rgb values when mouse move on image using MVVM..
Hope this will be help full for some one..
ViewModel:
class MainWindowViewModel : ViewModelBase
{
private Bitmap Img;
public ICommand OpenImg { get; set; }
public MainWindowViewModel()
{
OpenImg = new RelayCommand(openImg, (obj) => true);
}
private void openImg(object obj = null)
{
OpenFileDialog op = new OpenFileDialog();
op.Title = "Select a picture";
op.Filter = "All supported graphics|*.jpg;*.jpeg;*.png;*.bmp;*.tiff|" +
"JPEG (*.jpg;*.jpeg)|*.jpg;*.jpeg|" +
"Portable Network Graphic (*.png)|*.png";
if (op.ShowDialog() == true)
{
ImgPath = op.FileName;
Img = new Bitmap(ImgPath);
}
}
private string _ImgPath;
public string ImgPath
{
get
{
return _ImgPath;
}
set
{
_ImgPath = value;
OnPropertyChanged("ImgPath");
}
}
private ICommand _mouseMoveCommand;
public ICommand MouseMoveCommand
{
get
{
if (_mouseMoveCommand == null)
{
_mouseMoveCommand = new RelayCommand(param => ExecuteMouseMove((MouseEventArgs)param));
}
return _mouseMoveCommand;
}
set { _mouseMoveCommand = value; }
}
private void ExecuteMouseMove(MouseEventArgs e)
{
System.Windows.Point p = e.GetPosition(((IInputElement)e.Source));
XY = String.Format("X: {0} Y:{1}", (int)p.X, (int)p.Y);
BitmapData bd = Img.LockBits(new Rectangle(0, 0, Img.Width, Img.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
unsafe
{
byte* ptr = (byte*)bd.Scan0;
int x = (int)p.X * 3;
int y = (int)p.Y * bd.Stride;
RGB = "R: "+ptr[x + y + 2].ToString() + " G: " + ptr[x + y + 1].ToString() + " B: " + ptr[x + y].ToString();
}
Img.UnlockBits(bd);
}
private string xy;
public string XY
{
get { return xy; }
set
{
xy = value;
OnPropertyChanged("XY");
}
}
private string rgb;
public string RGB
{
get { return rgb; }
set
{
rgb = value;
OnPropertyChanged("RGB");
}
}
}
MainWindow.xaml
<Window.Resources>
<vm:MainWindowViewModel x:Key="MainWindowViewModel"/>
</Window.Resources>
<Grid DataContext="{StaticResource MainWindowViewModel}">
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="*" />
<RowDefinition Height="25"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Menu FontSize="20">
<MenuItem Header="File">
<MenuItem Header="Open" Command="{Binding OpenImg}"/>
</MenuItem>
</Menu>
</Grid>
<Grid Grid.Row="1" Background="LightGray">
<Viewbox Margin="3,3,3,3">
<Image x:Name="img" Stretch="None" Source="{Binding ImgPath}"
Model:MouseBehaviour.MouseMoveCommand="{Binding MouseMoveCommand}">
</Image>
</Viewbox>
</Grid>
<Grid Grid.Row="2">
<StackPanel Orientation="Horizontal">
<TextBox Focusable="False" Text="{Binding XY}" Width="100"/>
<TextBox Focusable="False" Text="{Binding RGB}" Width="115"/>
</StackPanel>
</Grid>
</Grid>

Slide images in ListBox

I have collection of images in listBox
<ListBox Name="lb" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Row="0" ScrollViewer.HorizontalScrollBarVisibility="Auto"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding}" >
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</Trigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Image Source="{Binding}" Width="150" Height="100"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Visible item is only selected one.
How can I slide or scroll images? Or how manage what item should be selected if I drag cursor on visible image to the left or to the right?
I implemented something similar in past project
Here are some bits of the code. More or less it will work as expected, but you may always adjust it as per your needs
This implements behavior of bringing selected item to view by scrolling to desired position and also implement sliding of item/image to left or right.
Listbox/ItemsControl (Binding is not important but useful for dynamic items)
<ItemsControl ItemsSource="{Binding}">
<i:Interaction.Behaviors>
<behavior:BringSelectionToViewBehavior SelectedItem="{Binding CurrentPage,Mode=TwoWay}" />
</i:Interaction.Behaviors>
<ItemsControl.Template>
<ControlTemplate TargetType="{x:Type ItemsControl}">
<ScrollViewer HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Hidden">
<ItemsPresenter />
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"
IsItemsHost="True" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
BringSelectionToViewBehavior.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Animation;
using System.Windows.Media;
using System.Windows.Threading;
using System.Windows.Input;
using Microsoft.Expression.Interactivity;
namespace MyProject.Behaviors
{
public class BringSelectionToViewBehavior : Behavior<ItemsControl>
{
Queue<Point> velocityPoints = new Queue<Point>(4);
Point PreviousPoint { get; set; }
DispatcherTimer valocityTimer = new DispatcherTimer();
public object SelectedItem
{
get { return GetValue(SelectedItemProperty); }
set { SetValue(SelectedItemProperty, value); }
}
// Using a DependencyProperty as the backing store for SelectedItem. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SelectedItemProperty =
DependencyProperty.Register("SelectedItem", typeof(object), typeof(BringSelectionToViewBehavior), new UIPropertyMetadata(null, OnSelectedItemChanged));
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.AddHandler(ItemsControl.SizeChangedEvent, (SizeChangedEventHandler)OnWidthChanged);
AssociatedObject.LayoutUpdated += new EventHandler(AssociatedObject_LayoutUpdated);
AssociatedObject.ReleaseMouseCapture();
valocityTimer.Interval = TimeSpan.FromMilliseconds(20);
valocityTimer.Tick += valocityTimer_Tick;
}
ItemsPresenter itemsPresenter;
void AssociatedObject_LayoutUpdated(object sender, EventArgs e)
{
if (itemsPresenter == null)
{
itemsPresenter = AssociatedObject.FindChildrenByType<ItemsPresenter>().ToList().First();
AssociatedObject.MouseMove += AssociatedObject_MouseMove;
itemsPresenter.AddHandler(UIElement.MouseLeftButtonDownEvent, new MouseButtonEventHandler(ItemPresenter_MouseLeftButtonDown), false);
itemsPresenter.AddHandler(UIElement.MouseLeftButtonUpEvent, new MouseButtonEventHandler(ItemPresenter_MouseLeftButtonUp), true);
}
}
public void OnWidthChanged(object s, SizeChangedEventArgs e)
{
Dispatcher.BeginInvoke((Action<ItemsControl, object>)QueueAnimation, DispatcherPriority.Render, new[] { AssociatedObject, SelectedItem });
}
private void QueueAnimation(ItemsControl control, object item)
{
BringItemToView(item);
}
public static void OnSelectedItemChanged(DependencyObject s, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue == null)
return;
BringSelectionToViewBehavior behavior = s as BringSelectionToViewBehavior;
if (!behavior.valocityOverride.HasValue)
behavior.BringItemToView(e.NewValue);
}
private void BringItemToView(object item, EasingMode easeMode = EasingMode.EaseInOut)
{
if (item == null || AssociatedObject == null)
return;
FrameworkElement element = AssociatedObject.ItemContainerGenerator.ContainerFromItem(item) as FrameworkElement;
BringElementToView(element, easeMode);
}
private void BringElementToView(FrameworkElement element, EasingMode easeMode)
{
if (element == null)
return;
ScrollViewer scrollViewer = element.FindAncestorByType<ScrollViewer>();
if (scrollViewer != null)
{
Point relativePoint = element.TransformToAncestor(scrollViewer).Transform(new Point(0, 0));
ScrollToPosition(scrollViewer, relativePoint.X, relativePoint.Y, easeMode);
}
else
{
element.BringIntoView();
}
}
private void ScrollToPosition(ScrollViewer viewer, double x, double y, EasingMode easeMode)
{
Storyboard sb = new Storyboard();
if (y != 0)
{
DoubleAnimation vertAnim = new DoubleAnimation();
//if (applyEase)
vertAnim.EasingFunction = new CubicEase() { EasingMode = easeMode };
vertAnim.From = viewer.VerticalOffset;
vertAnim.By = y;
vertAnim.Duration = GetAnimationDuration(y);
sb.Children.Add(vertAnim);
Storyboard.SetTarget(vertAnim, viewer);
Storyboard.SetTargetProperty(vertAnim, new PropertyPath(BringSelectionToViewBehavior.VerticalOffsetProperty));
}
if (x != 0)
{
DoubleAnimation horzAnim = new DoubleAnimation();
horzAnim.From = viewer.HorizontalOffset;
horzAnim.By = x;
horzAnim.Duration = GetAnimationDuration(x);
horzAnim.EasingFunction = new CubicEase() { EasingMode = easeMode };
sb.Children.Add(horzAnim);
Storyboard.SetTarget(horzAnim, viewer);
Storyboard.SetTargetProperty(horzAnim, new PropertyPath(BringSelectionToViewBehavior.HorizontalOffsetProperty));
}
//overrideDuretion = false;
sb.Completed += new EventHandler(sb_Completed);
sb.Begin();
}
void sb_Completed(object sender, EventArgs e)
{
((sender as ClockGroup).Timeline as Storyboard).Remove();
valocityOverride = null;
}
double? valocityOverride;
private Duration GetAnimationDuration(double distanceToTravel)
{
double animTime = Math.Abs(distanceToTravel) * .7;
if (valocityOverride.HasValue && valocityOverride > 50)
{
animTime = Math.Abs(distanceToTravel) * (valocityOverride.Value / 1000);
}
if (animTime > 1500)
animTime = 1500;
if (animTime < 250)
animTime = 250;
return new Duration(TimeSpan.FromMilliseconds(animTime));
}
public static double GetVerticalOffset(DependencyObject obj)
{
return (double)obj.GetValue(VerticalOffsetProperty);
}
public static void SetVerticalOffset(DependencyObject obj, double value)
{
obj.SetValue(VerticalOffsetProperty, value);
}
// Using a DependencyProperty as the backing store for VerticalOffset. This enables animation, styling, binding, etc...
public static readonly DependencyProperty VerticalOffsetProperty =
DependencyProperty.RegisterAttached("VerticalOffset", typeof(double), typeof(BringSelectionToViewBehavior), new PropertyMetadata(new PropertyChangedCallback(OnVerticalChanged)));
public static double GetHorizontalOffset(DependencyObject obj)
{
return (double)obj.GetValue(HorizontalOffsetProperty);
}
public static void SetHorizontalOffset(DependencyObject obj, double value)
{
obj.SetValue(HorizontalOffsetProperty, value);
}
// Using a DependencyProperty as the backing store for HorizontalOffset. This enables animation, styling, binding, etc...
public static readonly DependencyProperty HorizontalOffsetProperty =
DependencyProperty.RegisterAttached("HorizontalOffset", typeof(double), typeof(BringSelectionToViewBehavior), new PropertyMetadata(new PropertyChangedCallback(OnHorizontalChanged)));
private static void OnVerticalChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ScrollViewer viewer = d as ScrollViewer;
viewer.ScrollToVerticalOffset((double)e.NewValue);
}
private static void OnHorizontalChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ScrollViewer viewer = d as ScrollViewer;
viewer.ScrollToHorizontalOffset((double)e.NewValue);
}
void valocityTimer_Tick(object sender, EventArgs e)
{
Point currentPoint = Mouse.GetPosition(AssociatedObject);
velocityPoints.Enqueue(currentPoint);
if (velocityPoints.Count > 4)
velocityPoints.Dequeue();
}
private void AssociatedObject_MouseMove(object sender, MouseEventArgs e)
{
if (itemsPresenter.IsMouseCaptured)
{
Point CurrentPoint = e.GetPosition(AssociatedObject);
Vector offset = PreviousPoint - CurrentPoint;
PreviousPoint = CurrentPoint;
if (offset.X != 0)
{
Viewer.ScrollToHorizontalOffset(Viewer.HorizontalOffset + offset.X);
}
}
}
public ScrollViewer Viewer { get; set; }
/// <summary>
/// Handles the MouseLeftButtonDown event of the ItemPresenter control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="System.Windows.Input.MouseButtonEventArgs"/> instance containing the event data.</param>
private void ItemPresenter_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
//Viewer.ApplyAnimationClock(BringSelectionToViewBehavior.HorizontalOffsetProperty, null);
Border bd = (e.OriginalSource as FrameworkElement).FindAncestorByType<Border>();
if (bd == null || (bd != null && bd.Name != "GripBarElement"))
{
Viewer = AssociatedObject.FindChildrenByType<ScrollViewer>().ToList().First();
PreviousPoint = e.GetPosition(AssociatedObject);
itemsPresenter.CaptureMouse();
velocityPoints.Clear();
valocityTimer.Start();
}
}
private double CalculateMouseSpeed()
{
if (velocityPoints.Count > 1)
{
Point first = velocityPoints.Dequeue();
return velocityPoints.Aggregate<Point, Point, double>(first, (s, pt) => new Point(s.X, pt.X), pt => pt.X - pt.Y);
}
return 0;
}
/// <summary>
/// Handles the MouseLeftButtonUp event of the ItemPresenter control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="System.Windows.Input.MouseButtonEventArgs"/> instance containing the event data.</param>
private void ItemPresenter_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (itemsPresenter.IsMouseCaptured)
{
if (Viewer == null)
return;
valocityTimer.Stop();
itemsPresenter.ReleaseMouseCapture();
double MouseSpeed = CalculateMouseSpeed() / 3;
double newtotal = 5 * MouseSpeed;
double newOffset = Viewer.HorizontalOffset + newtotal;
double maxOffset = itemsPresenter.ActualWidth - (itemsPresenter.ActualWidth / AssociatedObject.Items.Count);
if (newOffset < 0)
{
newtotal += 0 - newOffset;
}
else if (newOffset > maxOffset)
{
newtotal -= newOffset - maxOffset;
}
newOffset = Viewer.HorizontalOffset + newtotal;
int newIndex = (int)System.Math.Round(newOffset / (itemsPresenter.ActualWidth / AssociatedObject.Items.Count));
object objToSet = AssociatedObject.ItemsSource.OfType<object>().ElementAt(newIndex);
if (objToSet != null)
{
valocityOverride = MouseSpeed;
if (!object.Equals(SelectedItem, objToSet))
SelectedItem = objToSet;
BringItemToView(objToSet, EasingMode.EaseOut);
}
}
}
}
}
Give it a try and see if this can solve your issue.

WPF Drag Adorner fix movement and show feedback in Adorner

I'm trying to build Inventory like interface for project that I'm creating.
Idea is to have list of images that can be dragged to players as shown below:
Images are loaded from directory and displayed inside ListView, list of players is displayed in ListBox.
My XAML looks like this:
<Window x:Class="DynamicImagesDrag.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dynamicImagesDrag="clr-namespace:DynamicImagesDrag"
Title="MainWindow"
Height="405"
Width="719.162"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Window.Resources>
<dynamicImagesDrag:StringToImageConverter x:Key="StringToImageConverter" />
</Window.Resources>
<Grid>
<ListView Name="MyList"
ItemsSource="{Binding Images}"
PreviewMouseLeftButtonDown="UIElement_OnPreviewMouseLeftButtonDown"
PreviewMouseMove="UIElement_OnPreviewMouseMove"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
VerticalAlignment="Stretch"
HorizontalAlignment="Left"
Margin="10" Width="250">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<ContentPresenter/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<DockPanel Width="50" Height="50">
<DockPanel.Background>
<ImageBrush ImageSource="BG1.png"/>
</DockPanel.Background>
<Image Source="{Binding Path, Converter={StaticResource StringToImageConverter} }"
Height="32"
Width="32" />
</DockPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<ListBox ItemsSource="{Binding People}" HorizontalAlignment="Right" HorizontalContentAlignment="Stretch" Margin="10" VerticalAlignment="Stretch"
Width="200">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical" AllowDrop="True" PreviewDrop="UIElement_OnPreviewDrop">
<TextBlock Text="{Binding Name}" FontWeight="Bold" />
<ProgressBar Height="20" Value="{Binding Points}" Margin="0" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
Code behind:
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace DynamicImagesDrag
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow
{
private readonly ObservableCollection<MyImage> _images = new ObservableCollection<MyImage>();
public ObservableCollection<MyImage> Images
{
get { return _images; }
}
private readonly ObservableCollection<Person> _people = new ObservableCollection<Person>();
public ObservableCollection<Person> People { get { return _people; } }
public MainWindow()
{
InitializeComponent();
_people.Add(new Person() { Name = "Person1", Points = 10 });
_people.Add(new Person() { Name = "Person2", Points = 0 });
_people.Add(new Person() { Name = "Person3", Points = 40 });
string appPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
if (appPath != null)
{
string imagePath = Path.Combine(appPath, "Images");
if (Directory.Exists(imagePath))
{
var images = Directory
.EnumerateFiles(imagePath)
.Where(file => file.ToLower().EndsWith("jpg") || file.ToLower().EndsWith("png"))
.ToList();
foreach (string image in images)
{
_images.Add(new MyImage
{
Name = Path.GetFileName(image),
Path = image,
Points = Convert.ToInt32(Path.GetFileNameWithoutExtension(image))
});
}
}
}
}
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool GetCursorPos(ref Win32Point pt);
[StructLayout(LayoutKind.Sequential)]
internal struct Win32Point
{
public Int32 X;
public Int32 Y;
};
public static Point GetMousePosition()
{
Win32Point w32Mouse = new Win32Point();
GetCursorPos(ref w32Mouse);
return new Point(w32Mouse.X, w32Mouse.Y);
}
private void UIElement_OnPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
_startPoint = e.GetPosition(null);
}
#region Field and Properties
private bool _dragHasLeftScope;
private Point _startPoint;
public bool IsDragging { get; set; }
DragAdorner _adorner;
AdornerLayer _layer;
public FrameworkElement DragScope { get; set; }
#endregion // Field and Properties
private void UIElement_OnPreviewMouseMove(object sender, MouseEventArgs e)
{
// Ensure that the user does not drag by accident
if (e.LeftButton == MouseButtonState.Pressed && !IsDragging)
{
Point position = e.GetPosition(null);
if (Math.Abs(position.X - _startPoint.X) > SystemParameters.MinimumHorizontalDragDistance ||
Math.Abs(position.Y - _startPoint.Y) > SystemParameters.MinimumVerticalDragDistance)
{
StartDragInProcAdorner(e);
}
}
}
void DragScope_DragLeave(object sender, DragEventArgs e)
{
if (e.OriginalSource == DragScope)
{
Point p = e.GetPosition(DragScope);
Rect r = VisualTreeHelper.GetContentBounds(DragScope);
if (!r.Contains(p))
{
_dragHasLeftScope = true;
e.Handled = true;
}
}
}
void Window1_DragOver(object sender, DragEventArgs args)
{
if (_adorner == null) return;
_adorner.LeftOffset = args.GetPosition(DragScope).X /* - _startPoint.X */ ;
_adorner.TopOffset = args.GetPosition(DragScope).Y /* - _startPoint.Y */ ;
}
void DragScope_QueryContinueDrag(object sender, QueryContinueDragEventArgs e)
{
if (_dragHasLeftScope)
{
e.Action = DragAction.Cancel;
e.Handled = true;
}
}
private void StartDragInProcAdorner(MouseEventArgs e)
{
DragScope = Application.Current.MainWindow.Content as FrameworkElement;
bool previousDrop = DragScope.AllowDrop;
DragScope.AllowDrop = true;
try
{
DragEventHandler draghandler = Window1_DragOver;
DragScope.PreviewDragOver += draghandler;
DragEventHandler dragleavehandler = DragScope_DragLeave;
DragScope.DragLeave += dragleavehandler;
QueryContinueDragEventHandler queryhandler = DragScope_QueryContinueDrag;
DragScope.QueryContinueDrag += queryhandler;
DragScope.GiveFeedback+=DragScope_GiveFeedback;
FrameworkElement dr = MyList.ItemContainerGenerator.ContainerFromItem(MyList.SelectedItem) as FrameworkElement;
if (dr == null)
return;
_adorner = new DragAdorner(DragScope, dr, true, 0.5);
_layer = AdornerLayer.GetAdornerLayer(DragScope);
_layer.Add(_adorner);
IsDragging = true;
_dragHasLeftScope = false;
DataObject data = new DataObject(MyList.SelectedItem as MyImage);
DragDropEffects de = DragDrop.DoDragDrop(MyList, data, DragDropEffects.Move);
DragScope.AllowDrop = previousDrop;
AdornerLayer.GetAdornerLayer(DragScope).Remove(_adorner);
_adorner = null;
DragScope.DragLeave -= dragleavehandler;
DragScope.QueryContinueDrag -= queryhandler;
DragScope.PreviewDragOver -= draghandler;
IsDragging = false;
}
catch
{
DragScope.AllowDrop = previousDrop;
AdornerLayer.GetAdornerLayer(DragScope).Remove(_adorner);
_adorner = null;
IsDragging = false;
}
}
private void DragScope_GiveFeedback(object sender, GiveFeedbackEventArgs e)
{
}
private void UIElement_OnPreviewDrop(object sender, DragEventArgs e)
{
var stackPanel = sender as StackPanel;
if (stackPanel == null) return;
var student = stackPanel.DataContext as Person;
MyImage myImage = e.Data.GetData(typeof(MyImage)) as MyImage;
if (student != null) student.Points += myImage.Points;
}
}
public class StringToImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
try
{
return new BitmapImage(new Uri((string)value));
}
catch
{
return new BitmapImage();
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class Person : INotifyPropertyChanged
{
private string _name;
private int _points;
public string Name
{
get { return _name; }
set
{
if (value == _name) return;
_name = value;
OnPropertyChanged();
}
}
public int Points
{
get { return _points; }
set
{
if (value == _points) return;
_points = value;
if (_points >= 100)
{
_points -= 100;
Debug.WriteLine("100!");
}
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public class MyImage
{
public string Path { get; set; }
public string Name { get; set; }
public int Points { get; set; }
}
}
and DragAdorner (taken from http://www.infragistics.com/community/blogs/alex_fidanov/archive/2009/07/28/drag-amp-drop-with-datapresenter-family-controls.aspx)
class DragAdorner : Adorner
{
public DragAdorner(UIElement owner) : base(owner) { }
public DragAdorner(UIElement owner, UIElement adornElement, bool useVisualBrush, double opacity)
: base(owner)
{
_owner = owner;
VisualBrush _brush = new VisualBrush
{
Opacity = opacity,
Visual = adornElement
};
DropShadowEffect dropShadowEffect = new DropShadowEffect
{
Color = Colors.Black,
BlurRadius = 15,
Opacity = opacity
};
Rectangle r = new Rectangle
{
RadiusX = 3,
RadiusY = 3,
Fill = _brush,
Effect = dropShadowEffect,
Width = adornElement.DesiredSize.Width,
Height = adornElement.DesiredSize.Height
};
XCenter = adornElement.DesiredSize.Width / 2;
YCenter = adornElement.DesiredSize.Height / 2;
_child = r;
}
private void UpdatePosition()
{
AdornerLayer adorner = (AdornerLayer)Parent;
if (adorner != null)
{
adorner.Update(AdornedElement);
}
}
#region Overrides
protected override Visual GetVisualChild(int index)
{
return _child;
}
protected override int VisualChildrenCount
{
get
{
return 1;
}
}
protected override Size MeasureOverride(Size finalSize)
{
_child.Measure(finalSize);
return _child.DesiredSize;
}
protected override Size ArrangeOverride(Size finalSize)
{
_child.Arrange(new Rect(_child.DesiredSize));
return finalSize;
}
public override GeneralTransform GetDesiredTransform(GeneralTransform transform)
{
GeneralTransformGroup result = new GeneralTransformGroup();
result.Children.Add(base.GetDesiredTransform(transform));
result.Children.Add(new TranslateTransform(_leftOffset, _topOffset));
return result;
}
#endregion
#region Field & Properties
public double scale = 1.0;
protected UIElement _child;
protected VisualBrush _brush;
protected UIElement _owner;
protected double XCenter;
protected double YCenter;
private double _leftOffset;
public double LeftOffset
{
get { return _leftOffset; }
set
{
_leftOffset = value - XCenter;
UpdatePosition();
}
}
private double _topOffset;
public double TopOffset
{
get { return _topOffset; }
set
{
_topOffset = value - YCenter;
UpdatePosition();
}
}
#endregion
}
Drag works almost fine:
except adorner is visible only in source list and target list, it isn't displayed during whole drag.
My questions are:
How can I fix drag and drop to see adorner whole time?
How can I display image instead selectedItem inside adorner? Right now inside adorner is that brown background, I'd like to get only transparent image.
How can I show if dragtarget is correct inside adorner instead of changing cursor? I'd like to change opacity of adorner if target is correct.
I'd like to get drag and drop working with touch events, #KOTIX suggested using Gong WPF dragdrop, will it work fine on touch enabled screens?
Currently I'm setting AllowDrop on StackPanel inside ListBox ItemTemplate, should it stay there or maybe I should set in on ListBox?
I've searched over internet (including SO) for solution, but I couldn't find anything that fits my needs.
I found some great articles:
http://www.codeproject.com/Articles/37161/WPF-Drag-and-Drop-Smorgasbord
http://www.zagstudio.com/blog/488#.VgHPyxHtmkp
http://nonocast.cn/adorner-in-wpf-part-5-drag-and-drop/
https://blogs.claritycon.com/blog/2009/03/generic-wpf-drag-and-drop-adorner/
Last one was very interesting, but I wasn't able to modify it in a way to add points to players instead of moving items. In my case I want items on left to stay, I just want to update list on right based on dragged item.
I don't have all the answers yet but it may lead you to your goals.
How can I fix drag and drop to see adorner whole time?
Your DragLayer is Transparent, just set a value for the Background property of your Grid
How can I display image instead selectedItem inside adorner? Right now
inside adorner is that brown background, I'd like to get only
transparent image.
The best I've come to is to explore the VisualTree to get the Image inside the template :
//Get the `ListViewItem`
FrameworkElement dr = MyList.ItemContainerGenerator.ContainerFromItem(MyList.SelectedItem) as FrameworkElement;
//Explore the VisualTree to get the grand-child
//This should be refactored to a Func<UIElement,UIElement> to accord to templates changes
UIElement el = VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(dr, 0), 0) as UIElement;
//Create the DragAdorner using the found UIElement
_adorner = new DragAdorner(DragScope, el, true, 1d);
How can I show if dragtarget is correct inside adorner instead of
changing cursor? I'd like to change opacity of adorner if target is
correct.
To show some feedback, you need to... GiveFeedBack
Here is your handler :
private void DragScope_GiveFeedback(object sender, GiveFeedbackEventArgs e)
{
if (_adorner == null) return;
if (e.Effects == DragDropEffects.Copy)
{
_adorner.Opacity = 1d;
e.Handled = true;
}
else
{
_adorner.Opacity = 0.5d;
e.Handled = true;
}
}
Now, you have to set the desired effect in 2 places : the Person template and the DragLayer :
private void StackPanel_DragOver(object sender, DragEventArgs e)
{
e.Effects = DragDropEffects.Copy;
e.Handled = true;
}
void Window1_DragOver(object sender, DragEventArgs args)
{
if (_adorner == null) return;
_adorner.LeftOffset = args.GetPosition(DragScope).X;
_adorner.TopOffset = args.GetPosition(DragScope).Y;
if (!args.Handled)
args.Effects = DragDropEffects.Move;
}
In order for these to work, you have to allow those 2 effects when initiating the DragDrop :
DragDropEffects de = DragDrop.DoDragDrop(MyList, data, DragDropEffects.Move | DragDropEffects.Copy);
Also, to avoid cumulative opacity reduction, use 1 for the DragAdorner opacity in constructor.
I'd like to get drag and drop working with touch events, #KOTIX
suggested using Gong WPF dragdrop, will it work fine on touch enabled
screens?
This solution is fully native, you should get it to work with touch devices.
Currently I'm setting AllowDrop on StackPanel inside ListBox
ItemTemplate, should it stay there or maybe I should set in on
ListBox?
If your goal is to add MyImage.Points to the drop target, the AllowDrop must be set on the StackPanel.
I would suggest using Gong WPF dragdrop
This library always worked great for me and way better than built-in support. It also adds great support for MVVM design.
Good luck!

Unable to set property in usercontrol from viewmodel

I am trying to set a property in a usercontrol from a view model, the control is meant to capture and display the users signature.
I am having problems setting the property in the usercontrol from a view model.
Receiving the signature from the usercontrol into the view model works fine.
Any ideas on how to solve this would be of greatly appreciated.
My test code is below.
User control XAML:
<UserControl x:Class="mvvmSignature.ucSignature"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="212" d:DesignWidth="300" Background="#FFEFFCEF">
<Grid Height="210">
<InkCanvas MinHeight="73" HorizontalAlignment="Left" Margin="10,27,0,0" Name="inkCanvas1" VerticalAlignment="Top" MinWidth="278" LostMouseCapture="inkCanvas1_LostMouseCapture" />
<Button Content="Clear" Height="23" HorizontalAlignment="Left" Margin="33,142,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="buttonClear_Click" />
<Button Content="Load on user control" Height="23" Margin="143,143,36,45" Click="ButtonLoad_Click" />
</Grid>
</UserControl>
UserControl CodeBehind:
using System.IO;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Ink;
using System.Windows.Input;
namespace mvvmSignature
{
public partial class ucSignature : UserControl
{
public ucSignature()
{
InitializeComponent();
}
private void inkCanvas1_LostMouseCapture(object sender, MouseEventArgs e)
{
SetSignature();
}
private void SetSignature()
{
var sb = new StringBuilder();
using (var ms = new MemoryStream())
{
inkCanvas1.Strokes.Save(ms);
foreach (var item in ms.ToArray())
{
sb.AppendFormat("{0},",
item);
}
ms.Close();
}
var local = sb.ToString().Trim() + "¬¬¬";
local = local.Replace(",¬¬¬", string.Empty);
Signature = local;
}
private void LoadSignature(string signatureIn)
{
if (string.IsNullOrEmpty(signatureIn) || !signatureIn.Contains(",")) return;
var x = signatureIn.Split(',').Select(byte.Parse).ToArray();
if (!x.Any()) return;
using (var ms = new MemoryStream(x))
{
inkCanvas1.Strokes = new StrokeCollection(ms);
ms.Close();
}
}
public static DependencyProperty SignatureProperty =
DependencyProperty.Register("Signature", typeof(string),
typeof(ucSignature));
public string Signature
{
get { return (string) GetValue(SignatureProperty); }
set
{
SetValue(SignatureProperty, value);
LoadSignature(value);
}
}
private void ButtonLoad_Click(object sender, RoutedEventArgs e)
{
Signature = "0,136,3,3,6,72,16,69,53,70,53,17,0,0,128,63,31,9,17,0,0,0,0,0,0,240,63,10,237,2,186,1,135,240,12,39,128,97,109,28,4,156,51,90,235,138,135,131,227,95,107,253,119,193,35,104,195,186,246,103,1,195,179,59,78,134,195,179,92,167,188,180,76,206,211,192,77,175,108,211,28,53,136,32,51,41,156,210,3,148,109,22,188,163,105,195,188,11,92,11,184,122,101,188,166,182,124,53,106,153,90,44,217,71,15,64,102,115,59,52,205,105,1,53,128,76,166,179,73,156,202,107,0,195,248,122,106,153,64,22,185,164,210,3,51,154,64,102,51,8,5,174,105,52,128,204,102,19,57,156,215,41,90,69,162,103,1,128,76,128,19,40,4,6,1,102,153,205,96,22,105,156,206,101,53,180,89,230,83,88,4,202,107,0,179,38,120,122,215,52,195,248,120,179,101,25,158,80,195,179,41,156,210,107,50,180,205,38,144,25,156,214,105,52,153,97,249,165,166,101,52,180,205,32,48,0,135,231,164,231,113,153,131,40,225,168,4,6,1,1,128,89,160,22,105,164,214,207,135,32,56,110,3,52,153,192,96,19,80,0,11,68,6,1,105,11,76,2,215,135,50,142,28,77,48,253,160,180,225,232,12,2,107,52,128,205,38,115,91,68,214,105,51,153,77,112,238,30,77,102,80,9,148,214,1,1,154,76,230,64,38,80,8,12,2,101,50,180,205,11,70,29,128,225,236,63,0,153,77,96,19,9,141,154,207,102,153,204,230,19,24,12,209,105,154,64,96,19,89,148,204,0,76,166,118,121,132,204,77,38,179,56,13,170,3,51,128,64,109,24,126,104,154,32,54,121,148,202,103,52,153,77,96,9,144";
}
private void buttonClear_Click(object sender, RoutedEventArgs e)
{
inkCanvas1.Strokes.Clear();
SetSignature();
}
}
}
MainWindow XAML:
<Window xmlns:my="clr-namespace:mvvmSignature" x:Class="mvvmSignature.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ignore="http://www.ignore.com"
mc:Ignorable="d ignore"
Height="490"
Width="327"
DataContext="{Binding Main, Source={StaticResource Locator}}">
<StackPanel>
<my:ucSignature Signature ="{Binding Signature,Mode=TwoWay}" />
<Button Width ="140" Height ="30" Content ="Load On Main Page" Command="{Binding LoadOnMainPageCommand}" />
<TextBox FontSize="8"
Foreground="Purple"
Text="{Binding Signature,Mode=TwoWay}"
VerticalAlignment="Center"
HorizontalAlignment="Center"
TextWrapping="Wrap" Height="210" />
</StackPanel>
MainViewModel:
using System.Windows.Input;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
namespace mvvmSignature.ViewModel
{
public class MainViewModel : ViewModelBase
{
public ICommand LoadOnMainPageCommand { get; set; }
public const string WelcomeTitlePropertyName = "Signature";
private string _signature = string.Empty;
public string Signature
{
get
{
return _signature;
}
set
{
if (_signature == value)
{
return;
}
_signature = value;
RaisePropertyChanged(WelcomeTitlePropertyName);
}
}
public MainViewModel()
{
LoadOnMainPageCommand = new RelayCommand(LoadSignature);
}
private void LoadSignature()
{
Signature ="0,136,3,3,6,72,16,69,53,70,53,17,0,0,128,63,31,9,17,0,0,0,0,0,0,240,63,10,237,2,186,1,135,240,12,39,128,97,109,28,4,156,51,90,235,138,135,131,227,95,107,253,119,193,35,104,195,186,246,103,1,195,179,59,78,134,195,179,92,167,188,180,76,206,211,192,77,175,108,211,28,53,136,32,51,41,156,210,3,148,109,22,188,163,105,195,188,11,92,11,184,122,101,188,166,182,124,53,106,153,90,44,217,71,15,64,102,115,59,52,205,105,1,53,128,76,166,179,73,156,202,107,0,195,248,122,106,153,64,22,185,164,210,3,51,154,64,102,51,8,5,174,105,52,128,204,102,19,57,156,215,41,90,69,162,103,1,128,76,128,19,40,4,6,1,102,153,205,96,22,105,156,206,101,53,180,89,230,83,88,4,202,107,0,179,38,120,122,215,52,195,248,120,179,101,25,158,80,195,179,41,156,210,107,50,180,205,38,144,25,156,214,105,52,153,97,249,165,166,101,52,180,205,32,48,0,135,231,164,231,113,153,131,40,225,168,4,6,1,1,128,89,160,22,105,164,214,207,135,32,56,110,3,52,153,192,96,19,80,0,11,68,6,1,105,11,76,2,215,135,50,142,28,77,48,253,160,180,225,232,12,2,107,52,128,205,38,115,91,68,214,105,51,153,77,112,238,30,77,102,80,9,148,214,1,1,154,76,230,64,38,80,8,12,2,101,50,180,205,11,70,29,128,225,236,63,0,153,77,96,19,9,141,154,207,102,153,204,230,19,24,12,209,105,154,64,96,19,89,148,204,0,76,166,118,121,132,204,77,38,179,56,13,170,3,51,128,64,109,24,126,104,154,32,54,121,148,202,103,52,153,77,96,9,144";
}
}
}
I re-wrote portions of your ucSignature class to properly declare and use the Signature property in your code behind. Use this class definition instead, and let me know if it works any better.
public partial class ucSignature : UserControl
{
public ucSignature()
{
InitializeComponent();
}
private void inkCanvas1_LostMouseCapture(object sender, MouseEventArgs e)
{
var sb = new StringBuilder();
using (var ms = new MemoryStream())
{
inkCanvas1.Strokes.Save(ms);
foreach (var item in ms.ToArray())
{
sb.AppendFormat("{0},", item);
}
ms.Close();
}
var local = sb.ToString().Trim() + "¬¬¬";
local = local.Replace(",¬¬¬", string.Empty);
this.SetValue(SignatureProperty, local);
}
private void LoadSignature(string signatureIn)
{
if (string.IsNullOrEmpty(signatureIn) || !signatureIn.Contains(",")) return;
var x = signatureIn.Split(',').Select(byte.Parse).ToArray();
if (!x.Any()) return;
using (var ms = new MemoryStream(x))
{
inkCanvas1.Strokes = new StrokeCollection(ms);
ms.Close();
}
}
public static DependencyProperty SignatureProperty = DependencyProperty.Register(
"Signature",
typeof(string),
typeof(ucSignature),
new UIPropertyMetadata(OnSignaturePropertyChanged));
public static string GetSignature(ucSignature signature)
{
return (string)signature.GetValue(SignatureProperty);
}
public static void SetSignature(ucSignature signature, string value)
{
signature.SetValue(SignatureProperty, value);
}
private static void OnSignaturePropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
var signature = obj as ucSignature;
if (signature != null)
{
LoadSignature(args.NewValue.ToString());
}
}
private void ButtonLoad_Click(object sender, RoutedEventArgs e)
{
this.SetValue(SignatureProperty, "0,136,3,3,6,72,16,69,53,70,53,17,0,0,128,63,31,9,17,0,0,0,0,0,0,240,63,10,237,2,186,1,135,240,12,39,128,97,109,28,4,156,51,90,235,138,135,131,227,95,107,253,119,193,35,104,195,186,246,103,1,195,179,59,78,134,195,179,92,167,188,180,76,206,211,192,77,175,108,211,28,53,136,32,51,41,156,210,3,148,109,22,188,163,105,195,188,11,92,11,184,122,101,188,166,182,124,53,106,153,90,44,217,71,15,64,102,115,59,52,205,105,1,53,128,76,166,179,73,156,202,107,0,195,248,122,106,153,64,22,185,164,210,3,51,154,64,102,51,8,5,174,105,52,128,204,102,19,57,156,215,41,90,69,162,103,1,128,76,128,19,40,4,6,1,102,153,205,96,22,105,156,206,101,53,180,89,230,83,88,4,202,107,0,179,38,120,122,215,52,195,248,120,179,101,25,158,80,195,179,41,156,210,107,50,180,205,38,144,25,156,214,105,52,153,97,249,165,166,101,52,180,205,32,48,0,135,231,164,231,113,153,131,40,225,168,4,6,1,1,128,89,160,22,105,164,214,207,135,32,56,110,3,52,153,192,96,19,80,0,11,68,6,1,105,11,76,2,215,135,50,142,28,77,48,253,160,180,225,232,12,2,107,52,128,205,38,115,91,68,214,105,51,153,77,112,238,30,77,102,80,9,148,214,1,1,154,76,230,64,38,80,8,12,2,101,50,180,205,11,70,29,128,225,236,63,0,153,77,96,19,9,141,154,207,102,153,204,230,19,24,12,209,105,154,64,96,19,89,148,204,0,76,166,118,121,132,204,77,38,179,56,13,170,3,51,128,64,109,24,126,104,154,32,54,121,148,202,103,52,153,77,96,9,144");
}
private void buttonClear_Click(object sender, RoutedEventArgs e)
{
inkCanvas1.Strokes.Clear();
this.SetValue(SignatureProperty, string.Empty);
}
}
You need to set your MainViewModel as the DataContext of your MainWindow so the binding works. Unless you're setting it in the constructor, I don't see it being set anywhere. You can try something like this:
<Application.Resources>
<local:MainViewModel x:Key="MainViewModel" />
</Application.Resources>
DataContext="{StaticResource MainViewModel}"
I'm going to assume that it's the Signature property that you are trying to set, mainly because that's what your user control is all about AND that's where I see an issue.
After you set the Signature property, you call RaisePropertyChanged on a different property: the WelcomeTitlePropertyName property. You should be calling RaisePropertyChanged on the same property you set or else your UI (i.e. your user control) won't know about it.
For example, in your MainWindow XAML you placed a ucSignature element like this:
<my:ucSignature Signature ="{Binding Signature,Mode=TwoWay}" />
That part is fine, but in your ViewModel that property should be defined like this:
private string _signature = string.Empty;
public string Signature
{
get { return _signature; }
set
{
if (_signature == value)
return;
_signature = value;
RaisePropertyChanged(Signature);
}
}

Categories