Drag and Drop WPF ComboBox, Buttons, Radio Buttons, etc - wpf

I have a problem, i've already read tutorials, blogs, etc about drag and drop on WPF (i'm using VS10).
The problem is I need to have a toolbox with buttons,combobox, radio button,etc sothe user can drag it and drop it(copy) on a work space (canvas or whatever).
I managed to do drag and drop from textbox and images but that doesn't work for me, when i tried on buttons or combobox it just doesnt work, i assume it is cause of the click event by default, i don't know what the problem is tho. Here is what i've tried with a button.
<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>
<TextBox Height="22" HorizontalAlignment="Left" Margin="84,36,0,0" Name="textBox1" VerticalAlignment="Top" Width="103" Text="Drag" />
<TextBox Height="40" HorizontalAlignment="Left" Margin="225,136,0,0" Name="textBox3" VerticalAlignment="Top" Width="124" Text="Drop" />
<Label Content="DragLabel" Height="26" HorizontalAlignment="Left" Margin="284,36,0,0" Name="label1" VerticalAlignment="Top" Width="80" MouseDown="label1_MouseDown" />
<Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="84,122,0,0" Name="button1" VerticalAlignment="Top" Width="75" MouseDown="button1_MouseDown" AllowDrop="True" IsEnabled="True" Click="button1_Click" />
<Rectangle Height="100" HorizontalAlignment="Left" Margin="149,199,0,0" Name="rectangle1" Stroke="Black" VerticalAlignment="Top" Width="200" AllowDrop="True" Fill="#FFDCA1A1" />
</Grid>
My Code Behind ...
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void label1_MouseDown(object sender, MouseButtonEventArgs e)
{
Label lbl = (Label)sender;
DragDrop.DoDragDrop(lbl, lbl.Content, DragDropEffects.Copy);
}
private void button1_MouseDown(object sender, MouseButtonEventArgs e)
{
var dependencyObject = (Button)sender;
DragDrop.DoDragDrop(dependencyObject, dependencyObject, DragDropEffects.Move);
}
private void button1_Click(object sender, RoutedEventArgs e)
{
return;
}
}
Thank You in advance guys. Btw sry about my english :s...
Thx again!
Luis

Have you tried using the PreviewMouseDown event instead of MouseDown? Your code will get called before the Button can capture the click.
WPF elements normally use RoutedEvents which often have a corresponding "Preview" event that uses the Tunneling Routing Strategy, which will be sent to all parents before the element that actually raised the event. This allows you to perform your operation in response to the MouseDown before the Button gets a chance to try to execute a click action.

private void button1_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
var dependencyObject = (Button)sender;
DragDrop.DoDragDrop(dependencyObject, dependencyObject, DragDropEffects.Move);
}
will work as mentioned by Abe

Related

Same click event for both Button and Grid

I read about Routed events today and tried to provide same click event handler for both a normal button and a custom grid button. Stackpanel is handling the routed button click event, and to invoke same handler I am firing a click event from grid's mousedown event. Code is without error but not working as expected.
Button click brings the messagebox but clicking the grid with mouse does nothing.
<Window x:Class="RoutedEventPr.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="550" Width="525">
<StackPanel Background="Transparent" Button.Click="Button_Click_1">
<Grid Width="200" Height="100" Background="Aqua" MouseDown="Grid_MouseDown_1">
<Ellipse StrokeThickness="4" Width="200" Height="100" Fill="Beige"/>
<TextBlock Text="Press" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
<Button x:Name="SureBtn" Content="Pause !" Width="200" Margin="0 10 0 0"/>
<Image HorizontalAlignment="Center" VerticalAlignment="Center" Width="200" Height="200" Source="I://fancybtn.jpg"/>
<Label Content="Start the game"/>
</StackPanel>
private void Grid_MouseDown_1(object sender, MouseButtonEventArgs e)
{
RaiseEvent(new RoutedEventArgs(Button.ClickEvent, this));
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
MessageBox.Show("Are you sure !");
}
Button.Click event is routed event with bubbling strategy set to Bubble.
That means it will bubble up to visual parent till root until it was handled. In your case, you raise an event from Window, so it will bubble up from Window to its parent which is null.
You have to raise the event from child control of StackPanel so that it can bubble up to StackPanel.
XAML
<Grid x:Name="grid" Width="200" Height="100" Background="Aqua"
MouseDown="Grid_MouseDown_1">
<Ellipse StrokeThickness="4" Width="200" Height="100" Fill="Beige"/>
<TextBlock Text="Press" HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Grid>
Code behind
private void Grid_MouseDown_1(object sender, MouseButtonEventArgs e)
{
grid.RaiseEvent(new RoutedEventArgs(Button.ClickEvent, this));
}
I did below changes and it works now.
private void Grid_MouseDown_1(object sender, MouseButtonEventArgs e)
{
SureBtn.RaiseEvent(new RoutedEventArgs(Button.ClickEvent, this));
}

Deleting dynamically created button from ListBox in WPF

I'm attempting to delete a dynamically created button from a listbox using the ListBox.Items.Remove, but I keep getting the error of "Operation is not valid while ItemsSource is in use. Access and modify elements with ItemsControl.ItemsSource instead." Problem is, ItemsControl.ItemsSource is not a valid option in my code.
A little run-down on the code: I have a MainWindow that contains a ListBox and the "Add" and "Delete" buttons. Adding a button sends you to a window where you can input a firstname and lastname. Clicking "Done" adds the newly-created profile's Button to the Listbox (you can access the profile by clicking on said button). I didn't include the Profile code as its empty except for the firstname and lastname being bound to labels there.
How would I access/modify the the button/profile in order to delete them? I know it has to do with the databinding, but I'm thoroughly confused on how to delete the item.
Any help would be much appreciated. I've included the MainWindow and ProfileCreator code below.
<Window x:Class="SavingButtons.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">
<Window.Resources>
<DataTemplate x:Key="UserTemplate">
<StackPanel Orientation="Horizontal">
<Button Name="TestAddButton" Click="TestAddButton_Clicked" Content="{Binding FirstName}" Width="100" Height="40"></Button>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<Button Name="AddProfileButton" Content="Add Profile" HorizontalAlignment="Left" Margin="22,29,0,0" VerticalAlignment="Top" Width="75" Click="AddProfileButton_Click"/>
<ListBox Name="ButtonHoldersListbox" IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding}" ItemTemplate="{StaticResource UserTemplate}" HorizontalAlignment="Left" Height="202" Margin="22,69,0,0" VerticalAlignment="Top" Width="183" />
<Button Name="DeleteUserButton" Click="DeleteUserButton_Click" Content="Delete User" HorizontalAlignment="Left" Margin="246,69,0,0" VerticalAlignment="Top" Width="105"/>
</Grid>
namespace SavingButtons
{
public partial class MainWindow : Window
{
NewProfile np;
public int buttonNumberID;
public MainWindow()
{
InitializeComponent();
np = new NewProfile(this);
}
private void AddProfileButton_Click(object sender, RoutedEventArgs e)
{
np.Show();
}
//adds button to listbox
internal void TestAddButton_Clicked(object sender, RoutedEventArgs e)
{
Button cmd = (Button)sender;
if (cmd.DataContext is User)
{
//Profile is where the finished information is displayed//
Profile pro = new Profile();
pro.DataContext = cmd.DataContext;
pro.Show();
}
}
//this is where confusion ensues
private void DeleteUserButton_Click(object sender, RoutedEventArgs e)
{
//error occurs here
ButtonHoldersListbox.Items.Remove(ButtonHoldersListbox.SelectedItem);
}
}
}
The Profile Creator:
<Window x:Class="SavingButtons.NewProfile"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="NewProfile" Height="300" Width="500">
<Grid>
<Label Content="FirstName" HorizontalAlignment="Left" Margin="64,44,0,0" VerticalAlignment="Top"/>
<Label Content="LastName" HorizontalAlignment="Left" Margin="64,97,0,0" VerticalAlignment="Top"/>
<Button Name="UploadImageButton" Click="UploadImageButton_Click" Content="Upload Image" HorizontalAlignment="Left" Margin="64,146,0,0" VerticalAlignment="Top" Width="75"/>
<TextBox Name="FirstNameTextBox" HorizontalAlignment="Left" Height="23" Margin="126,47,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="120"/>
<TextBox Name="LastNameTextBox" HorizontalAlignment="Left" Height="23" Margin="126,99,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="120"/>
<Image Name="imgPhoto" HorizontalAlignment="Left" Height="100" Margin="173,146,0,0" VerticalAlignment="Top" Width="100"/>
<Button Name="ProfileFinishedLaunch" Content="Done" HorizontalAlignment="Left" Margin="360,232,0,0" VerticalAlignment="Top" Width="75" Click="ProfileFinishedLaunch_Click"/>
</Grid>
namespace SavingButtons
{
public partial class NewProfile : Window
{
public ObservableCollection<User> ProfileList;
public MainWindow mMain;
public NewProfile(MainWindow main)
{
InitializeComponent();
ProfileList = new ObservableCollection<User>();
mMain = main;
}
//loads image
private void UploadImageButton_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog op = new OpenFileDialog();
op.Title = "Select a picture";
op.Filter = "All supported graphics|*.jpg;*.jpeg;*.png|" +
"JPEG (*.jpg;*.jpeg)|*.jpg;*.jpeg|" +
"Portable Network Graphic (*.png)|*.png";
if (op.ShowDialog() == true)
{
imgPhoto.Source = new BitmapImage(new System.Uri(op.FileName));
}
}
//creates a new user out of all the info, inserts new user into the collection, adds new button
private void ProfileFinishedLaunch_Click(object sender, RoutedEventArgs e)
{
mMain.buttonNumberID++;
ProfileList.Add(new User { FirstName = FirstNameTextBox.Text, LastName = LastNameTextBox.Text, imgPhoto = imgPhoto.Source });
mMain.ButtonHoldersListbox.DataContext = ProfileList;
mMain.Show();
this.Hide();
}
You are setting yourListbox` to the others window property and you do it every time after a new item was added.
The error occurs, because the listbox items were set through binding to the ItemsSource property and in this case the ListBox.Items is read only so you can't remove or add item directly.
Instead of what you have now, add an ObservableCollection<User> property to your MainWindow class and bind the ListBox to this property. In the NewProfile window you need to add the new User item to this collection. The delete operation will work with removing the item from that collection (actually the senders DataContext)
public partial class MainWindow : Window
{
public ObservableCollection<User> Profiles {get; set;}
//...
private void DeleteUserButton_Click(object sender, RoutedEventArgs e)
{
var removable = ButtonHoldersListbox.SelectedItem as User;
if(removable != null)
Profiles.Remove(removable);
}
}
<ListBox Name="ButtonHoldersListbox" IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding Profiles}" ItemTemplate="{StaticResource UserTemplate}" HorizontalAlignment="Left" Height="202" Margin="22,69,0,0" VerticalAlignment="Top" Width="183" />
public partial class NewProfile : Window
{
//creates a new user out of all the info, inserts new user into the collection, adds new button
private void ProfileFinishedLaunch_Click(object sender, RoutedEventArgs e)
{
mMain.buttonNumberID++;
var newUser = new User { FirstName = FirstNameTextBox.Text, LastName = LastNameTextBox.Text, imgPhoto = imgPhoto.Source };
mMain.Profiles.Add(newUser);
//Don't set the listbox.DataContext here
mMain.Show();
this.Hide();
}
if you set itemsource to usercontrol you can't operate it's items directly. Edit it's itemsource instead. give you a simple example.
public partial class MainWindow : Window
{
ObservableCollection<int> ProfileList;
public MainWindow()
{
InitializeComponent();
ProfileList = new ObservableCollection<int>();
this.DataContext = ProfileList;
}
private void btnAdd_Click(object sender, RoutedEventArgs e)
{
Random r = new Random();
int num = r.Next(100);
ProfileList.Add(num);
//lstShow.Items.Add(num); error!
}
private void btnDel_Click(object sender, RoutedEventArgs e)
{
if (lstShow.SelectedIndex > -1)
{
ProfileList.Remove((int)lstShow.SelectedItem);
//lstShow.Items.Remove((int)lstShow.SelectedItem); error!
}
}
}
Thanks to Miklos, I did get my problem solved, however, the binding is still pretty confusing. Mainly: how does the ListBox know to bind the ObservableCollection ProfileList? In Mikalos version, he explicitly binds the ObservableCollection to the Listbox in the XAML(NOTE: Mikalos observable collection is named "Profile")
ItemsSource="{Binding Profiles}"
That would seem the most explicit. Instead, the only way I was only able to make it work was this way(ProfileList is the name I used for the observable collection):
ItemsSource="{Binding}"
Not sure how it knows to bind to my observableCollection. I will include my working code below.
<Window x:Class="SavingButtons.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">
<Window.Resources>
<DataTemplate x:Key="UserTemplate">
<StackPanel Orientation="Horizontal">
<Button Name="TestButton" Click="cmdDeleteUser_Clicked" Content="{Binding FirstName}" Width="100" Height="40"></Button>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<Button Name="AddProfileButton" Content="Add Profile" HorizontalAlignment="Left" Margin="22,29,0,0" VerticalAlignment="Top" Width="75" Click="AddProfileButton_Click"/>
<ListBox Name="ButtonHoldersListbox" IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding}" ItemTemplate="{StaticResource UserTemplate}" HorizontalAlignment="Left" Height="202" Margin="22,69,0,0" VerticalAlignment="Top" Width="183" />
<Button Name="DeleteUserButton" Click="DeleteUserButton_Click" Content="Delete User" HorizontalAlignment="Left" Margin="246,69,0,0" VerticalAlignment="Top" Width="105"/>
</Grid>
My ProfileCreator Cs:
public partial class NewProfile : Window
{
public MainWindow mMain;
public NewProfile(MainWindow main)
{
InitializeComponent();
mMain = main;
}
//creates a new user out of all the info, inserts new user into the collection, adds new button
private void ProfileFinishedLaunch_Click(object sender, RoutedEventArgs e)
{
////Mikalos CODE-----------------------------------------------------------//
var newUser = new User { FirstName = FirstNameTextBox.Text, LastName = LastNameTextBox.Text, imgPhoto = imgPhoto.Source };
mMain.ProfileList.Add(newUser);
mMain.ButtonHoldersListbox.DataContext = mMain.ProfileList;//Mikalo suggested not putting ListBox.DataContext here,
//however, this is the only place it works.
mMain.Show();
this.Hide();
//---------------------------------------------------------------//
}

How to change button text from OK/CANCEL to YES/NO for MessageBox in SL4?

SL 4 provides a dialog box by MessageBox, but MessageBoxButton only provide option for button as OK, Cancel. How to change it to YES, NO button?
This MessageBox built into silverlight can't be changed beyond the capabilities that are exposed.
Your only solution would be to make a custom ChildWindow class which provides the functionality you want. There are many examples of this.
This has the advantage of acting more like other silverlight popup windows, and can be themed and skinned however you'd like, with whatever buttons and functionality you chose to implement.
This has the disadvantage that you are forced then to use a callback model rather than an a more usual imperative flow control.
Your best bet is to use the System.Windows.Controls.Primitives.Popup
<Grid x:Name="LayoutRoot" Background="White">
<Button x:Name="showPopup" Click="showPopup_Click" Height="100" Width="100" Content="Show Popup"/>
<Popup x:Name="myPopup" IsOpen="False" VerticalAlignment="Top" HorizontalAlignment="Center" >
<Canvas Height="200" Width="300" Background="Azure">
<Button x:Name="closePopup" Click="closePopup_Click" Height="50" Width="100" Content="Close Popup"/>
</Canvas>
</Popup>
<Canvas x:Name="myCanvas" Visibility="Collapsed" Background="Black" Opacity=".4"></Canvas>
</Grid>
public partial class Page : UserControl
{
public Page()
{
InitializeComponent();
}
private void closePopup_Click(object sender, RoutedEventArgs e)
{
myPopup.IsOpen = false;
myCanvas.Visibility = Visibility.Collapsed;
}
private void showPopup_Click(object sender, RoutedEventArgs e)
{
myPopup.IsOpen = true;
myCanvas.Visibility = Visibility.Visible;
}
}
If you don't want to create your own popup, there probably are 3rd party messageboxes, but with this solution, you have eveything in your own hands.

Seemingly strange WPF behaviour: textbox lostfocus event and button click event

The following behaviour is occurring with the .net 4.0 code set out further below:
Click on textbox so it gains focus, and then click button:
As code stands, lostfocus handler is called, but not buttonclick handler
Comment out MessageBox.Show("handlelostfocus") and then click handler is called
Set breakpoint in handlelostfocus and breakpoint is hit, but click handler is not called
Are these bugs or behaviour by design - if later, is there any further explanation?
<Window x:Class="WpfApplication4.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>
<Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="216,194,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" />
<TextBox Height="23" HorizontalAlignment="Left" Margin="197,108,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" />
</Grid>
</Window>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
textBox1.LostFocus += new RoutedEventHandler(handlelostfocus);
}
private void handlelostfocus(object sender, RoutedEventArgs e)
{
MessageBox.Show("handlelostfocus");
}
private void button1_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("click");
}
}
Change ClickMode property of Button to "Press"
<Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="216,194,0,0" ClickMode="Press" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" MouseUp="button1_MouseUp" MouseLeftButtonUp="button1_MouseLeftButtonUp" />
<TextBox Height="23" HorizontalAlignment="Left" Margin="197,108,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" />
The 'click' in this case never occurs because as H.B. indicated you are interrupting the UI/event logic by showing the modal messagebox so there is never a mouse down event on the button.
Try replacing the messagebox with a non-modal wndow such as this:
new Window() { Width = 300, Height = 100, Title = "handlelostfocus" }.Show();
and you will see that the events still occur because you are not drawing the focus away from the main window in the middle of the event logic.
You interrupt the click logic, to get a click both mouse-down and its mouse-up need to occur consecutively on the Button; thus the observed behavior seems fine to me.

Any way for a ToolTip to trigger a MouseEnter event?

I have a control with a tooltip. What i want is that when I hover over the control and the tooltip opens: if I then enter the tooltip with the mouse - this will trigger a mouseEnter event in order to trigger some other action. The closest I am to finding a solution to this is adding a ToolTipClosing event on the control with the trigger ... but this will fire as soon as I leave the control - even if my mouse doesn't enter the tooltip.
(Triggering a MouseEnter event on the tooltip itself doesn't seem to get fired at all)
Here's an example: (where I want to change the background of the border if I enter the tooltip)
XAML
<Border Height="300" Name="dummyBorder"
Width="200"
Background="Red" />
<Label ToolTipService.InitialShowDelay="3000"
Content="Hover over here"
ToolTipService.ShowDuration="4000"
ToolTipService.Placement="Right"
ToolTipClosing="Label_ToolTipClosing"
Width="100"
HorizontalAlignment="Center"
Margin="10">
<Label.ToolTip>
<ToolTip Name="tt" MouseEnter="ttBorder_MouseEnter">
<Border Background="Brown"
Name="ttBorder"
MouseEnter="ttBorder_MouseEnter"
Width="100"
Height="50">
<TextBlock Text="This is a tool tip." />
</Border>
</ToolTip>
</Label.ToolTip>
</Label>
CodeBehind: (neither of these work)
private void Label_ToolTipClosing(object sender, ToolTipEventArgs e)
{
if (tt.IsMouseDirectlyOver)
{
dummyBorder.Background = Brushes.Aqua;
}
}
private void ttBorder_MouseEnter(object sender, MouseEventArgs e)
{
dummyBorder.Background = Brushes.Aqua;
}
I specifically want to use a tooltip and not a popup. Is this possible?
Any help will be greatly appreciated!
you will want to try something like this:
<Window.CommandBindings>
<CommandBinding Command="ChangeColour"
CanExecute="ChangeCanExecute"
Executed="ChangeExecuted" />
</Window.CommandBindings>
inside your tooltip tag:
<MouseBinding Gesture="LeftClick" Command="{Binding ChangeColour}"/>
then in your codebehind:
private void ChangeCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
e.Handled = true;
}
private void ChangeExecuted(object sender, ExecutedRoutedEventArgs e)
{
dummyBorder.Background = Brushes.Aqua;
e.Handled = true;
}
Well (after 9 months and no answer) I guess that there is no way then. (Unless proven otherwise)

Resources