Got a WPF application which has an on hover popup. The popup contains a list of different files which can be opened (e.g. pdf, excel etc)
You can navigate and select a file by double clicking and it opens as you would expect.
But if I now navigate to a different file I can see that the on hover selection isn't now working,
If you now select a different file, the original file is opened again.
I am using a Process.Start and passing the full path to the file to the method.
The application is a fair size so here are some excerpts for a Test application I have written to look into this further
The XAML for the main window
<Window x:Class="TestPopupIssue.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">
<Canvas Margin="5" Background="Red" Width="200" Height="150" >
<Rectangle Name="rectangle1"
Canvas.Top="60" Canvas.Left="50"
Height="85" Width="60"
Fill="Black" MouseEnter="rectangle1_MouseEnter" MouseLeave="rectangle1_MouseLeave" />
<Popup x:Name="PopupWindow" PlacementTarget="{Binding ElementName=rectangle1}" Placement="Top" MouseEnter="rectangle1_MouseEnter" MouseLeave="rectangle1_MouseLeave">
<ListBox MinHeight="50" ItemsSource="{Binding Files}" MouseDoubleClick="FileList_MouseDoubleClick"`enter code here` x:Name="FileList" />
</Popup>
</Canvas>
</Window>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
FileList f;
public MainWindow()
{
InitializeComponent();
f = new FileList();
f.PopulateFiles();
this.DataContext = f;
}
private void FileList_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
if (FileList.SelectedItem != null)
{
string item = FileList.SelectedItem as string;
if (item != null)
{
System.Diagnostics.Process.Start(item);
}
}
}
private void rectangle1_MouseEnter(object sender, MouseEventArgs e)
{
PopupWindow.IsOpen = true;
}
private void rectangle1_MouseLeave(object sender, MouseEventArgs e)
{
PopupWindow.IsOpen = false;
}
}
And there is a FileList class which just has a generic string list of file paths called
Files
Thanks
I have tested your Sample-Application, when your opening the File with Process.Start your Focus gets stolen by the Application that opens your File.
Somehow the ListBox in the Popup canĀ“t change their SelectedItem when the Window has lost his Focus.
Unfortunately I have not managed to get the focus back on the Window, this.SetFocus() has not worked for me.
Anyway another possible Solution would be to close the Popup when your opening the File.
private void FileList_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
if (FileList.SelectedItem != null)
{
string item = FileList.SelectedItem as string;
if (item != null)
{
System.Diagnostics.Process.Start(item);
PopupWindow.IsOpen = false;
}
}
}
this way the ListBox can update the selectedItem again.
hope this helps!
Related
i have a window that is acting like a Dialog with two buttons and a textbox, so its an input dialog, the problem is when i press the buttons, or close the dialog window in any way the main window that i used to create this dialog will get closed too! (exit code = 0) and another thing is that it will work OK in vs debugging but when i run the app without vs debugging that owner window get closed and in my case that window is the Main app window so the whole application will shutdown! what am i doing wrong?
XAML:
<Window x:Class="Server.Forms.Dialogs.PokeDialog"
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:local="clr-namespace:Server.Forms.Dialogs"
mc:Ignorable="d"
Title="Poke" MinHeight="120" MinWidth="225"
ResizeMode="CanResizeWithGrip"
SizeToContent="WidthAndHeight"
x:Name="windowPoke"
Loaded="windowPoke_Loaded">
<StackPanel VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
Margin="25">
<Label Content="Poke Reason:"
Padding="0"/>
<TextBox x:Name="textboxPokeReason"
Margin="0 5"/>
<DockPanel>
<Button x:Name="btnOk"
Click="btnOk_Click"
IsDefault="True"
Content="OK"
MinWidth="100"
Margin="0 0 5 0"/>
<Button x:Name="btnCancel"
Click="btnCancel_Click"
IsCancel="True"
Content="Cancel"
MinWidth="100"/>
</DockPanel>
</StackPanel>
C#:
public partial class PokeDialog : Window
{
public string PokeReason
{
get { return textboxPokeReason.Text; }
}
public PokeDialog()
{
InitializeComponent();
}
private void windowPoke_Loaded(object sender, RoutedEventArgs e)
{
textboxPokeReason.Focus();
}
private void btnOk_Click(object sender, RoutedEventArgs e)
{
DialogResult = true;
}
private void btnCancel_Click(object sender, RoutedEventArgs e)
{
DialogResult = false;
}
}
the way i used dialog:
private void btnPokeClient_Click(object sender, RoutedEventArgs e)
{
var dialog = new PokeDialog();
if (dialog.ShowDialog() == true)
{
MessageBox.Show("TRUE - input text:" + dialog.PokeReason);
}
else MessageBox.Show("FALSE");
}
i can see the messageboxes too, but after closing this message boxes the whole app will get closed
I'm trying to achieve one Login button which changes to a Please wait... mode once pressed. I was thinking about some StoryBoard animation inside button or GIF image animation. Seems I'm pretty new to WPF and after several search attempts, I'm unable to find a fully working idea.
Login button idea
What would be the best way to do the above thing? Is there any way to eliminate the GIF image and create the same using some Path or something similar? Also note that I want this behavior of button to be triggered on Button.Click event.
Just simply add WpfAnimatedGif from your visual studio Tools -> NuGet Package Manager->Package Manager Console. There you find the Package adder window and type pm > Install-Package WpfAnimatedGif. After a few seconds package should be added. Now firstly, you have add namespace xmlns:gif="http://wpfanimatedgif.codeplex.com" then design your Button.
<Window x:Class="WpfApplication1.MainWindow" Name="root"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:gif="http://wpfanimatedgif.codeplex.com"
Width="500" Height="500" DataContext="{Binding ElementName=root}">
<StackPanel>
<Button Name="BtnLogin" Width="100" Height="30" Content="Login" Background="Red" Margin="0,0,0,5" Click="Button_Click"/>
<Button Width="100" Height="30" Background="Red">
<Grid>
<Image gif:ImageBehavior.AnimatedSource="{Binding DataContext.LoadingImage}" HorizontalAlignment="Left" VerticalAlignment="Center"/>
<TextBlock Text="Please Wait" Padding="30,0,0,0" HorizontalAlignment="Right" VerticalAlignment="Center"/>
</Grid>
</Button>
</StackPanel>
</Window>
The following .cs file is below and when you need to stop loading image, just assign LoadingImage = null;
public partial class MainWindow : Window,INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
}
private ImageSource _LoadingImage;
public ImageSource LoadingImage
{
get { return _LoadingImage; }
set
{
_LoadingImage = value;
OnPropertyChanged("LoadingImage");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
LoadingImage = GetBitmapImage("/aaaa.gif");
}
public static BitmapImage GetBitmapImage(String location)
{
BitmapImage image = null;
try
{
Uri iconUri = new Uri("pack://application:,,,/" + ";component" + location, UriKind.RelativeOrAbsolute);
image = new BitmapImage(iconUri);
}
catch (Exception ex)
{
}
return image;
}
}
Simple question though. I have a WPF application (.NET 4.0). There is a listbox which contains a number of userpanels. Each of these userpanels contains a checkbox.
When running you can click any portion of the userpanel except the checkbox itself and the listbox will select that row (which is indicated visually by the background changing in this simple case). If you check the box the row is not selected.
Requirement:
If you check the checkbox, this should count as selecting the row.
Checkbox Control:
<UserControl x:Class="CheckboxClickExample.CheckboxControl"
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="189" d:DesignWidth="221">
<Grid>
<CheckBox Content="CheckBox" Height="16" HorizontalAlignment="Left" Margin="10,10,0,0" Name="checkBox1" VerticalAlignment="Top" />
</Grid>
</UserControl>
Main Window:
<Window x:Class="CheckboxClickExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:checkboxsample="clr-namespace:CheckboxClickExample"
Title="MainWindow" Height="350" Width="525">
<ListBox>
<checkboxsample:CheckboxControl/>
<checkboxsample:CheckboxControl/>
<checkboxsample:CheckboxControl/>
<checkboxsample:CheckboxControl/>
</ListBox>
</Window>
You could handle this in your UserControl code behind:
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
var parent = sender as DependencyObject;
while (parent != null)
{
if (parent is Selector)
break;
parent = VisualTreeHelper.GetParent(parent);
}
if (parent != null)
((Selector) parent).SelectedItem = this;
}
And then use the handler in your CheckBox:
<CheckBox Content="CheckBox"
Height="16"
Click="ButtonBase_OnClick"
HorizontalAlignment="Left"
Margin="10,10,0,0"
VerticalAlignment="Top" />
Edit
If you don't want to use code behind, the best I think you can do is to package the existing solution as an attached behaviour. This has the benefit that you only have to write the code once, and that the property can be set on any button even if it is not part of a UserControl.
For example:
public static class ButtonClickHelper
{
public static void SetEnableSelectionOnClick(ButtonBase button, bool value)
{
button.SetValue(EnableSelectionOnClickProperty, value);
}
public static bool GetEnableSelectionOnClick(ButtonBase button)
{
return (bool) button.GetValue(EnableSelectionOnClickProperty);
}
public static readonly DependencyProperty EnableSelectionOnClickProperty =
DependencyProperty.RegisterAttached("EnableSelectionOnClick", typeof (bool), typeof (ButtonClickHelper),
new FrameworkPropertyMetadata(OnEnableSelectionOnClickPropertyChanged));
private static void OnEnableSelectionOnClickPropertyChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
if (!(d is ButtonBase))
return;
var button = (ButtonBase) d;
if ((bool) e.NewValue)
{
button.Click += OnButtonClick;
}
else
{
button.Click -= OnButtonClick;
}
}
private static void OnButtonClick(object sender, RoutedEventArgs e)
{
var parent = sender as DependencyObject;
var ancestors = new List<DependencyObject>();
while (parent != null)
{
if (parent is Selector)
break;
parent = VisualTreeHelper.GetParent(parent);
ancestors.Add(parent);
}
if (parent != null)
{
var selector = (Selector) parent;
var itemToSelect = ancestors.Where(i => selector.Items.Contains(i)).FirstOrDefault();
if (itemToSelect != null)
((Selector) parent).SelectedItem = itemToSelect;
}
}
}
Then you can use this in your XAML by just setting the EnableSelectionOnClick dependency property:
<CheckBox Content="CheckBox"
Height="16"
l:ButtonClickHelper.EnableSelectionOnClick="True"
HorizontalAlignment="Left"
Margin="10,10,0,0"
VerticalAlignment="Top" />
Hope this helps!
This question already has answers here:
SurfaceScrollViewer: getting touch on nested children
(2 answers)
Closed 2 years ago.
I have been banging my head on this for a while now! Here is my simple User Control:
<UserControl x:Class="DaCapo.MyUserControl"
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:s="http://schemas.microsoft.com/surface/2008"
IsManipulationEnabled="True"
Width="300" Height="300">
<Canvas>
<s:SurfaceButton x:Name="button" Width="100" Height="100" Content="Click!" Style="{x:Null}"/>
<Popup x:Name="popup" Width="200" Height="100" IsOpen="False" StaysOpen="True" PlacementRectangle="0,0,200,100"
AllowsTransparency="True" Focusable="True">
<DockPanel x:Name="dockpanel" Width="200" Height="100" Background="SteelBlue" Focusable="True"/>
</Popup>
</Canvas>
</UserControl>
I want to be able to detect touches on the DockPanel or in a possible child of it. Here follows the code behind for the same class, with the alternatives I attempted:
public partial class MyUserControl : UserControl
{
public MyUserControl()
{
InitializeComponent();
TouchExtensions.AddHoldGestureHandler(this.button, this.HoldHandler);
/* NONE OF THE FOLLOWING WORKS */
// TouchExtensions.AddTapGestureHandler(this.popup, this.TapHandler);
// this.dockpanel.TouchDown += new System.EventHandler<TouchEventArgs>(popup_TouchDown);
// this.popup.TouchDown += new System.EventHandler<TouchEventArgs>(popup_TouchDown);
// this.popup.ManipulationStarting += new System.EventHandler<ManipulationStartingEventArgs>(popup_ManipulationStarting);
// this.dockpanel.ManipulationStarting += new System.EventHandler<ManipulationStartingEventArgs>(popup_ManipulationStarting);
}
void popup_ManipulationStarting(object sender, ManipulationStartingEventArgs e) { Debug.WriteLine("Tap..."); }
void popup_TouchDown(object sender, TouchEventArgs e) { Debug.WriteLine("Tap..."); }
private void TapHandler(object sender, TouchEventArgs e) { Debug.WriteLine("Tap..."); }
private void HoldHandler(object sender, TouchEventArgs e) { Debug.WriteLine("Holding..."); this.popup.IsOpen = true; }
}
I do believe I am missing something obvious. Can someone please help me? Thanks.
The Button & popup needs to be connected to its click (or touch) handlers defined in the code behind, in XAML itself.
If you want to handle touch events for the dockpanel, How about
adding a button inside the dockpanel with opacity = 0 ??
EDIT :
I can see that you defined a few handlers, but did you add those Handlers to the button?
For example, for a SurfaceButton :
IN XAML :
<s:SurfaceButton Click="OpenButton_Click"/>
Correspondingly connects the function in C# as:
private void OpenButton_Click(object sender, RoutedEventArgs e)
{
// Handle the action for the click here.
}
Under the covers, Popup creates another hwnd to render its content into. This is different from all other WPF controls. You need to register this hwnd with the Surface SDK so it will start sending touch events to it. Use this to do that: http://msdn.microsoft.com/en-us/library/microsoft.surface.presentation.input.touchextensions.enablesurfaceinput.aspx
I'm doing a sample with MVVM and have a problem with commands. I have an Article class (with ID, Name, Price, etc.), an ArticleViewModel that represents the view model, and a user control (ArticleControl) that allows to input the data for the article, with bindings to the properties of the ArticleViewModel. This user control has a biding for a save command.
<UserControl.CommandBindings>
<CommandBinding x:Name="saveCmd"
Command="local:Commands.Save"
CanExecute="CommandBinding_CanExecute"
Executed="CommandBinding_Executed"/>
</UserControl.CommandBindings>
This is how the command is defined:
public class Commands
{
private static RoutedUICommand _save;
public static RoutedUICommand Save
{
get { return _save; }
}
static Commands()
{
InputGestureCollection saveInputs = new InputGestureCollection();
saveInputs.Add(new KeyGesture(Key.S, ModifierKeys.Control, "Ctrl+S"));
_save = new RoutedUICommand(
"Save",
"Save",
typeof(Commands),
saveInputs);
}
}
And the command binding handlers:
private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
double baseprice = 0;
double.TryParse(ArticleBasePrice.Text, out baseprice);
e.CanExecute =
!string.IsNullOrEmpty(ArticleID.Text) &&
!string.IsNullOrEmpty(ArticleName.Text) &&
!string.IsNullOrEmpty(ArticleDescription.Text) &&
baseprice > 0;
}
private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
{
ArticleViewModel avm = (ArticleViewModel)DataContext;
if (avm != null && avm.Save())
{
ArticleID.Text = String.Empty;
ArticleName.Text = String.Empty;
ArticleDescription.Text = String.Empty;
ArticleBasePrice.Text = String.Empty;
}
}
Now, I put this user control on a window. When I hit Ctrl+S the command is executed. However, I also put a Save button on that window, next to this user control. When I click it I want to execute the same command (and I don't want to do another command binding in the window where the user control is hosted).
<StackPanel>
<local:ArticleControl x:Name="articleControl" />
<Button Name="btnSave"
Content="Save" Width="100"
HorizontalAlignment="Left"
Command="{???}"/> <!-- what should I put here? -->
</StackPanel>
But I do not know how to refer that saveCmd defined in the user control. I tried different things, some are completely wrong (they throw exception when running the app), some don't have any effect.
Command="{StaticResource saveCmd}"
Command="{StaticResource local:ArticleControl.saveCmd}"
Command="{x:Static local:Commands.Save}"
Any help is appreciated. Thank you.
The reason why the Save button will not cause the commandbindings of your other control to execute is because the Save button is outside the user control and therefore the command system will not look for a commandbinding in that control. The Command execution strategy is a bit like a bubbling event and will start from the focused item (the Button) and go up the visual tree until it finds the CommandBindings.
You can either implement the command binding in the parent control or set the CommandTarget property of the Save button to the user control.
Another approach is to set the FocusManager.IsFocusScope=True on the button or the container of the button. If you do this I suggest you read up on what IsFocusScope does but in a nutshell it will leave the input focus on whatever control has the focus when you press the button, instead of making the button the new input focus. This is generally used for toolbars or menu like structures.
Based on Patrick's suggestions, this is what I did:
Put the command binding in the user control and implemented the handlers in the code-behind as shown in the original message.
Used Command, CommandTarget and FocusManager properties on the button to point to the binding from the user control (ArticleUserControl is the x:Name of the user control).
This is how the XAML for the window looks:
<Window x:Class="MVVMModel.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MVVMModel"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<local:ArticleControl x:Name="articleControl" />
<Button Name="btnSave" Content="Save" Width="100" HorizontalAlignment="Left"
Command="local:Commands.Save"
CommandTarget="{Binding ElementName=ArticleUserControl}"
FocusManager.IsFocusScope="True" />
</StackPanel>
</Window>
I think you just have to move your CommandBinding to a Resource Dictionary, so that it's available outside your UserControl!
Here is what I did to work, though I'm not particularly happy with the solution. If anyone knows a better approach, please do let me know.
I moved the logic for the commands handler in a separate, static class:
static class CommandsCore
{
public static bool Save_CanExecute(ArticleControl ac)
{
double baseprice = 0;
double.TryParse(ac.ArticleBasePrice.Text, out baseprice);
return
!string.IsNullOrEmpty(ac.ArticleID.Text) &&
!string.IsNullOrEmpty(ac.ArticleName.Text) &&
!string.IsNullOrEmpty(ac.ArticleDescription.Text) &&
baseprice > 0;
}
public static void Save_Executed(ArticleControl ac)
{
ArticleViewModel avm = (ArticleViewModel)ac.DataContext;
if (avm != null && avm.Save())
{
ac.ArticleID.Text = String.Empty;
ac.ArticleName.Text = String.Empty;
ac.ArticleDescription.Text = String.Empty;
ac.ArticleBasePrice.Text = String.Empty;
}
}
}
I kept the command binding in the user control as it was
<UserControl.CommandBindings>
<CommandBinding x:Name="saveCmd"
Command="local:Commands.Save"
CanExecute="CommandBinding_CanExecute"
Executed="CommandBinding_Executed"/>
</UserControl.CommandBindings>
But in the handlers I called the two methods I just defined above.
public void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = CommandsCore.Save_CanExecute(this);
}
public void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
{
CommandsCore.Save_Executed(this);
}
And then I did the same from the window where the control is used.
<Window x:Class="MVVMModel.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MVVMModel"
Title="MainWindow" Height="350" Width="525">
<Window.CommandBindings>
<CommandBinding x:Name="saveCmd"
Command="local:Commands.Save"
CanExecute="CommandBinding_CanExecute"
Executed="CommandBinding_Executed"/>
</Window.CommandBindings>
<StackPanel>
<local:ArticleControl x:Name="articleControl" />
<Button Name="btnSave" Content="Save" Width="100" HorizontalAlignment="Left"
Command="local:Commands.Save"/>
</StackPanel>
</Window>
and the handlers
public void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = CommandsCore.Save_CanExecute(articleControl);
}
public void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
{
CommandsCore.Save_Executed(articleControl);
}
And this works, the Save button is enabled only when the fields are filled in appropriately and the command is executed correctly when clicking the button.