Dragging an image from the desktop and dropping it to bitmap viewer - wpf

I want to drag an image (person image) from the desktop and then dropping it to my wpf application
any resources ?

XAML:
<Window x:Class="_13378018.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" AllowDrop="True" Drop="OnDrop">
<Grid>
<Image x:Name="imageViewer"/>
</Grid>
</Window>
Code-behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
private BitmapImage LoadImageFromFile(string filename)
{
using (var fs = File.OpenRead(filename))
{
var img = new BitmapImage();
img.BeginInit();
img.CacheOption = BitmapCacheOption.OnLoad;
// Downscaling to keep the memory footprint low
img.DecodePixelWidth = (int)SystemParameters.PrimaryScreenWidth;
img.StreamSource = fs;
img.EndInit();
return img;
}
}
private void OnDrop(object sender, DragEventArgs e)
{
var data = e.Data as DataObject;
if (data.ContainsFileDropList())
{
var files = data.GetFileDropList();
imageViewer.Source = LoadImageFromFile(files[0]);
}
}
}

Related

Why the trigger doesn't work after GridSplitter is dragged

Due to project privacy protection, I can't paste the product code here. So I made a simple example to show the problem.
I have a window like this:
When I click the Right button, I want the Right Column hide and the Left Column Stretch to full window:
Here is my Xaml:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="0"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition x:Name="rightCol" MinWidth="0"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<Button Height="100" Content="Left" />
</StackPanel>
<GridSplitter Grid.Column="1" Width="10" VerticalAlignment="Stretch" HorizontalAlignment="Center"/>
<StackPanel x:Name="right" Grid.Column="2" >
<Button Height="100" Content="Right" Click="Button_Click" />
</StackPanel>
</Grid>
</Window>
And here is my code-behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
var b = new Binding("Visibility")
{
Source = this.right
};
var style = new Style(typeof(ColumnDefinition))
{
Setters =
{
new Setter(ColumnDefinition.WidthProperty, new GridLength(300)),
new Setter(ColumnDefinition.MinWidthProperty, 0.0),
}
};
style.Triggers.Add(new DataTrigger()
{
Binding = b,
Value = System.Windows.Visibility.Collapsed,
Setters =
{
new Setter(ColumnDefinition.WidthProperty, new GridLength(0)),
new Setter(ColumnDefinition.MaxWidthProperty, double.PositiveInfinity),
new Setter(ColumnDefinition.MinWidthProperty, 0.0),
}
});
this.rightCol.Style = style;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
this.right.Visibility = System.Windows.Visibility.Collapsed;
}
}
When I run it and Click the Right button, it works fine. But if I drag the GridSplitter before I Click the Right button, the trigger doesn't work:
Why this happened?
Note:
If I use below code, it can work.
private void Button_Click(object sender, RoutedEventArgs e)
{
this.right.Visibility = System.Windows.Visibility.Collapsed;
this.rightCol.Width = new GridLength(0);
}
But is there any way to use trigger?
I got my answer from this link:
GridSplitter overrides ColumnDefinition's style trigger?
So below code works fine:
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
var b = new Binding("Visibility")
{
Source = this.rightPane,
Mode = BindingMode.OneWay
};
var style = new Style(typeof(ColumnDefinition))
{
Setters =
{
new Setter(ColumnDefinition.WidthProperty, new GridLength(300)),
new Setter(ColumnDefinition.MinWidthProperty, 0.0),
}
};
/*
style.Triggers.Add(new DataTrigger()
{
Binding = b,
Value = System.Windows.Visibility.Collapsed,
Setters =
{
new Setter(ColumnDefinition.WidthProperty, new GridLength(0)),
new Setter(ColumnDefinition.MaxWidthProperty, double.PositiveInfinity),
new Setter(ColumnDefinition.MinWidthProperty, 0.0),
}
});*/
Storyboard sb = new Storyboard();
ObjectAnimationUsingKeyFrames oaf = new ObjectAnimationUsingKeyFrames();
DiscreteObjectKeyFrame disobj = new DiscreteObjectKeyFrame();
disobj.KeyTime = TimeSpan.FromMilliseconds(0);
disobj.Value = GridLength.Auto;
oaf.KeyFrames.Add(disobj);
sb.Children.Add(oaf);
Storyboard.SetTargetProperty(oaf, new PropertyPath(ColumnDefinition.WidthProperty));
var actionBegin = new BeginStoryboard();
actionBegin.Storyboard = sb;
actionBegin.Name="BeginStoryboard1";
var actionEnd = new RemoveStoryboard();
actionEnd.BeginStoryboardName = "BeginStoryboard1";
var trigger = new DataTrigger()
{
Binding = b,
Value = System.Windows.Visibility.Collapsed
};
trigger.EnterActions.Add(actionBegin);
trigger.ExitActions.Add(actionEnd);
style.Triggers.Add(trigger);
style.RegisterName(actionBegin.Name, actionBegin);
this.rightCol.Style = style;
}

OxyPlot WPF not working with Button Click

I’m having some problems with OxyPlot that I have not been able to resolve through their documentation or other searches. I’m working on a wpf application that will allow the user to open a .csv with a button-click event, then perform some math and report back some useful information. I’d like to plot some of the generated data hence OxyPlot. For some reason I cannot get the plot to populate, when the code that generates it, is within the button click event. To illustrate here is a smaller example:
This code works (xaml):
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:oxy="http://oxyplot.org/wpf"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Button x:Name="button" Content="Button" HorizontalAlignment="Left" Margin="20,20,0,0" VerticalAlignment="Top" Width="75" Click="button_Click"/>
<Grid HorizontalAlignment="Left" Height="255" Margin="20,47,0,0" VerticalAlignment="Top" Width="477">
<oxy:PlotView Model="{Binding ScatterModel}"/>
</Grid>
</Grid>
with this:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
var tmp = new PlotModel { Title = "Scatter plot", Subtitle = "y = x" };
var s2 = new LineSeries
{
StrokeThickness = 1,
MarkerSize = 1,
MarkerStroke = OxyColors.ForestGreen,
MarkerType = MarkerType.Plus
};
for (int i = 0; i < 100; i++)
{
s2.Points.Add(new DataPoint(i, i));
}
tmp.Series.Add(s2);
this.ScatterModel = tmp;
}
private void button_Click(object sender, RoutedEventArgs e)
{
}
public PlotModel ScatterModel { get; set; }
And produces this:
Plot Working
But, without changing the xaml, if I copy/paste the code beneath the button click event:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void button_Click(object sender, RoutedEventArgs e)
{
DataContext = this;
var tmp = new PlotModel { Title = "Scatter plot", Subtitle = "y = x" };
var s2 = new LineSeries
{
StrokeThickness = 1,
MarkerSize = 1,
MarkerStroke = OxyColors.ForestGreen,
MarkerType = MarkerType.Plus
};
for (int i = 0; i < 100; i++)
{
s2.Points.Add(new DataPoint(i, i));
}
tmp.Series.Add(s2);
this.ScatterModel = tmp;
}
public PlotModel ScatterModel { get; set; }
The plot never generates: Not working:
I’ve tried moving DataContext = this; back up to public MainWindow(), and vice-versa with InitializeComponent(); no change. I’ve also tried defining
<Window.DataContext>
<local:MainWindow/>
</Window.DataContext>
in the xaml but that throws an exception/infinite loop error during build.
Something simple I fear I'm not getting about OxyPlot implementation?
Thanks!
CSMDakota
INotifyPropertyChanged keeps your view in sync with the program's state. One way to do this is by implementing a ViewModel (the MVVM pattern).
So let's create one. ViewModelBase introduces OnPropertyChanged(), the method that updates ScatterModel.
ViewModels.cs
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using OxyPlot;
namespace WpfApplication1
{
public class ViewModel : ViewModelBase
{
private PlotModel _scatterModel;
public PlotModel ScatterModel
{
get { return _scatterModel; }
set
{
if (value != _scatterModel)
{
_scatterModel = value;
OnPropertyChanged();
}
}
}
}
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] String propName = null)
{
// C#6.O
// PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
if (PropertyChanged != null)
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propName));
}
}
}
In MainWindow.xaml you can now add
<Window.DataContext>
<local:ViewModel/>
</Window.DataContext>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void button_Click(object sender, RoutedEventArgs e)
{
var tmp = new PlotModel { Title = "Scatter plot", Subtitle = "y = x" };
var s2 = new LineSeries
{
StrokeThickness = 1,
MarkerSize = 1,
MarkerStroke = OxyColors.ForestGreen,
MarkerType = MarkerType.Plus
};
for (int i = 0; i < 100; i++)
{
s2.Points.Add(new DataPoint(i, i));
}
tmp.Series.Add(s2);
ViewModel.ScatterModel = tmp;
}
// C#6.O
// public ViewModel ViewModel => (ViewModel)DataContext;
public ViewModel ViewModel
{
get { return (ViewModel)DataContext; }
}
}
Note we're no longer setting DataContext = this, which is considered bad practice. In this case the ViewModel is small, but as a program grows this way of structuring pays off.

WPF: Animation is not smooth

I am animating a TextBlock. In 60 seconds, it increases FontSize from 8pt to 200pt. Everything is working fine, except that my animation is moving up and down a bit as the text grows. Why is this happening and is it possible to avoid this?
I have a very simple XAML file:
<Window x:Class="Timer.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="800"
Height="500"
Title="MainWindow"
Loaded="Window_Loaded">
<Grid>
<TextBlock
Name="TimerTextBlock"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="00h : 00m : 00.000s" />
</Grid>
</Window>
And equally simple code-behind:
public partial class MainWindow : Window
{
private const string timerFormat = "{0:hh'h : 'mm'm : 'ss'.'fff's'}";
private DispatcherTimer dispatcherTimer;
private DateTime targetTime;
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
targetTime = DateTime.Now.AddSeconds(60);
double totalTime = targetTime.Subtract(DateTime.Now).TotalMilliseconds;
DoubleAnimation animation = new DoubleAnimation();
animation.From = TimerTextBlock.FontSize;
animation.To = 200;
animation.Duration = new Duration(targetTime.Subtract(DateTime.Now));
TimerTextBlock.BeginAnimation(TextBlock.FontSizeProperty, animation);
dispatcherTimer = new DispatcherTimer();
dispatcherTimer.Interval = TimeSpan.FromMilliseconds(1);
dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
dispatcherTimer.Start();
}
private void dispatcherTimer_Tick(object sender, EventArgs e)
{
if (DateTime.Compare(targetTime, DateTime.Now) > 0)
{
TimerTextBlock.Text =
string.Format(timerFormat, targetTime.Subtract(DateTime.Now));
}
}
}
Thank you for all the clarifications.
Your vertical jumping problem is due to font rendering rounding. Specifically, WPF will avoid subpixel font height in order to enable font smoothing. One way to avoid this is to convert your text into a path geometry and then use a scale transform to animate it.
Here is an alternate version of your example without the jumping. The new XAML is:
<Grid>
<Path Name="Path" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
and the new code when you load the window:
SetText("");
var transform = new ScaleTransform(1, 1);
Path.LayoutTransform = transform;
var animationX = new DoubleAnimation(1, 10, new Duration(TimeSpan.FromSeconds(60)));
transform.BeginAnimation(ScaleTransform.ScaleXProperty, animationX);
var animationY = new DoubleAnimation(1, 10, new Duration(TimeSpan.FromSeconds(60)));
transform.BeginAnimation(ScaleTransform.ScaleYProperty, animationY);
and a new method to set the text that is anmiated:
private void SetText(string text)
{
var formatted = new FormattedText(text, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface("Lucida Console"), 12, Brushes.Black);
Path.Data = formatted.BuildGeometry(new Point(0, 0));
Path.Fill = Brushes.Black;
}
and you have call SetText from your timer event handler.
Note that to avoid horizontal jumpiness, you have to use a fixed-length text string and a constant-width font.

WPF 4 Drag and Drop with visual element as cursor

I have a WPF 4 app which I want to enable drag and drop with, currently I have it working with a basic drag and drop implementation, but I have found that it would be much better if, instead of the mouse cursor changing over to represent the move operation, I could use an image underneath my finger.
My drag and drop operation is initiated inside a custom user control, so I will need to insert a visual element into the visual tree and have it follow my finger around, perhaps I should enable the ManipulationDelta event on my main window, check for a boolean then move the item around?
From the mentioned article I was able to simplify a little. Basically what you need to do is subscribe in 3 events:
PreviewMouseLeftButtonDownEvent: The event that runs when you press the left button, you can start the drag action by invoking DragDrop.DoDragDrop
DropEvent: The event that runs when you drop something (control must have AllowDrop set to true in order to accept drops)
GiveFeedbackEvent: The event that runs all the time allowing you to give constant feedback
DragDrop.DoDragDrop(draggedItem, draggedItem.DataContext, DragDropEffects.Move); the first parameter is the element you are dragging, then the second is the data it is carrying and last the mouse effect.
This method locks the thread. So everything after its call will only execute when you stop dragging.
In the drop event you can retrieve the data you sent on the DoDragDrop call.
The source for my tests are located bellow, and the result is:
Sample drag n' drop (gif)
Full Source
MainWindow.xaml
<Window x:Class="TestWpfPure.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:uc="clr-namespace:TestWpfPure"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ListBox x:Name="CardListControl" AllowDrop="True" ItemsSource="{Binding Items}" />
</Grid>
</Window>
Card.xaml
<UserControl x:Class="TestWpfPure.Card"
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="300" d:DesignWidth="300">
<Grid>
<Border x:Name="CardBorder" BorderBrush="Black" BorderThickness="3" HorizontalAlignment="Left" Height="40" VerticalAlignment="Top" Width="246" RenderTransformOrigin="0.5,0.5" CornerRadius="6">
<TextBlock Text="{Binding Text}" TextWrapping="Wrap" FontFamily="Arial" FontSize="14" />
</Border>
</Grid>
</UserControl>
MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Effects;
using System.Windows.Shapes;
namespace TestWpfPure
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public ObservableCollection<Card> Items { get; set; }
private readonly Style listStyle = null;
private Window _dragdropWindow = null;
public MainWindow()
{
InitializeComponent();
Items = new ObservableCollection<Card>(new List<Card>
{
new Card { Text = "Task #01" },
new Card { Text = "Task #02" },
new Card { Text = "Task #03" },
new Card { Text = "Task #04" },
new Card { Text = "Task #05" },
});
listStyle = new Style(typeof(ListBoxItem));
listStyle.Setters.Add(new Setter(ListBoxItem.AllowDropProperty, true));
listStyle.Setters.Add(new EventSetter(ListBoxItem.PreviewMouseLeftButtonDownEvent, new MouseButtonEventHandler(CardList_PreviewMouseLeftButtonDown)));
listStyle.Setters.Add(new EventSetter(ListBoxItem.DropEvent, new DragEventHandler(CardList_Drop)));
listStyle.Setters.Add(new EventSetter(ListBoxItem.GiveFeedbackEvent, new GiveFeedbackEventHandler(CardList_GiveFeedback)));
CardListControl.ItemContainerStyle = listStyle;
DataContext = this;
}
protected void CardList_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (sender is ListBoxItem)
{
var draggedItem = sender as ListBoxItem;
var card = draggedItem.DataContext as Card;
card.Effect = new DropShadowEffect
{
Color = new Color { A = 50, R = 0, G = 0, B = 0 },
Direction = 320,
ShadowDepth = 0,
Opacity = .75,
};
card.RenderTransform = new RotateTransform(2.0, 300, 200);
draggedItem.IsSelected = true;
// create the visual feedback drag and drop item
CreateDragDropWindow(card);
DragDrop.DoDragDrop(draggedItem, draggedItem.DataContext, DragDropEffects.Move);
}
}
protected void CardList_Drop(object sender, DragEventArgs e)
{
var droppedData = e.Data.GetData(typeof(Card)) as Card;
var target = (sender as ListBoxItem).DataContext as Card;
int targetIndex = CardListControl.Items.IndexOf(target);
droppedData.Effect = null;
droppedData.RenderTransform = null;
Items.Remove(droppedData);
Items.Insert(targetIndex, droppedData);
// remove the visual feedback drag and drop item
if (this._dragdropWindow != null)
{
this._dragdropWindow.Close();
this._dragdropWindow = null;
}
}
private void CardList_GiveFeedback(object sender, GiveFeedbackEventArgs e)
{
// update the position of the visual feedback item
Win32Point w32Mouse = new Win32Point();
GetCursorPos(ref w32Mouse);
this._dragdropWindow.Left = w32Mouse.X;
this._dragdropWindow.Top = w32Mouse.Y;
}
private void CreateDragDropWindow(Visual dragElement)
{
this._dragdropWindow = new Window();
_dragdropWindow.WindowStyle = WindowStyle.None;
_dragdropWindow.AllowsTransparency = true;
_dragdropWindow.AllowDrop = false;
_dragdropWindow.Background = null;
_dragdropWindow.IsHitTestVisible = false;
_dragdropWindow.SizeToContent = SizeToContent.WidthAndHeight;
_dragdropWindow.Topmost = true;
_dragdropWindow.ShowInTaskbar = false;
Rectangle r = new Rectangle();
r.Width = ((FrameworkElement)dragElement).ActualWidth;
r.Height = ((FrameworkElement)dragElement).ActualHeight;
r.Fill = new VisualBrush(dragElement);
this._dragdropWindow.Content = r;
Win32Point w32Mouse = new Win32Point();
GetCursorPos(ref w32Mouse);
this._dragdropWindow.Left = w32Mouse.X;
this._dragdropWindow.Top = w32Mouse.Y;
this._dragdropWindow.Show();
}
[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;
};
}
}
Card.xaml.cs
using System.ComponentModel;
using System.Windows.Controls;
namespace TestWpfPure
{
/// <summary>
/// Interaction logic for Card.xaml
/// </summary>
public partial class Card : UserControl, INotifyPropertyChanged
{
private string text;
public string Text
{
get
{
return this.text;
}
set
{
this.text = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Text"));
}
}
public Card()
{
InitializeComponent();
DataContext = this;
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
There is an example of using a custom drag cursor at Jaime Rodriguez msdn blog. You can handle the GiveFeedback event and change the mouse cursor, but to use a custom Visual the author creates a new Window and updates the position on QueryContinueDrag.

How to create silverlight sequence animation programmatically?

I need to animate a rectangle to move horizontally first, then after 2 second make it move vertically. All this should be done programmatically.
Anybody can help me? Thanks!
Using the following XAML:
<UserControl x:Class="SilverlightApplication1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Canvas x:Name="LayoutRoot">
<Rectangle x:Name="myBox" Fill="Red" Height="100" Width="100" Canvas.Left="0" Canvas.Top="0" />
</Canvas>
</UserControl>
You could create the animation programatically using this:
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
Loaded += MainPage_Loaded;
}
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
var moveAnimation = CreateAnimation(this.myBox);
moveAnimation.Begin();
}
public Storyboard CreateAnimation(FrameworkElement element)
{
var storyboard = new Storyboard();
var downAnimation = new DoubleAnimationUsingKeyFrames();
Storyboard.SetTarget(downAnimation, element);
Storyboard.SetTargetProperty(downAnimation, new PropertyPath(Canvas.TopProperty));
downAnimation.KeyFrames.Add(new EasingDoubleKeyFrame
{
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(2)),
Value = 200
});
storyboard.Children.Add(downAnimation);
var overAnimation = new DoubleAnimationUsingKeyFrames();
Storyboard.SetTarget(overAnimation, element);
Storyboard.SetTargetProperty(overAnimation, new PropertyPath(Canvas.LeftProperty));
overAnimation.KeyFrames.Add(new EasingDoubleKeyFrame
{
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(2)),
Value = 0
});
overAnimation.KeyFrames.Add(new EasingDoubleKeyFrame
{
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(4)),
Value = 200
});
storyboard.Children.Add(overAnimation);
return storyboard;
}
}

Resources