WPF Tooltip that works with touch - c#

I have a WPF application where tooltip is bound to a viewmodel, and all is great when I hover over my control and wait for the tooltip.
To support mouseless devices, I would like to show the same tooltip, when "clicking" my control.
If I implement a MouseDown handler that sets the tooltip's IsOpen = true, the tooltip is shown, but binding has not been evaluated, unless I hover and wait first.
XAML:
<Window x:Class="TooltipApplication.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:local="clr-namespace:TooltipApplication"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Image Source="http://www.brockmann-consult.de/beam/doc/help/visat/images/icons/Help22.png" MouseDown="Image_MouseDown" Stretch="None" MouseLeave="Image_MouseLeave">
<Image.ToolTip>
<ToolTip>
<TextBlock Text="{Binding Hint}"/>
</ToolTip>
</Image.ToolTip>
</Image>
</Grid>
</Window>
Code behind:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace TooltipApplication
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
public string Hint { get { return "This is my hint"; } }
private void Image_MouseDown(object sender, MouseButtonEventArgs e)
{
var tooltip = (sender as FrameworkElement).ToolTip;
if (tooltip is ToolTip)
((ToolTip)tooltip).IsOpen = true;
}
private void Image_MouseLeave(object sender, MouseEventArgs e)
{
var tooltip = (sender as FrameworkElement).ToolTip;
if (tooltip is ToolTip)
((ToolTip)tooltip).IsOpen = false;
}
}
}
Using the code above, click the image before the tooltip timer elapses, or run it on a touch device.
Placing a breakpoint in the Hint getter also proves, that it is not called unless you wait for the tooltip timer.
How can I force the tooltip to evaluate its binding, and then show?

Related

WPF dragblz Finding TabItem to Re-Add

ok Im not sure if this issue is related to the library or me being still being new to WPF .but , I'm using the dragablz library and I am trying to re-add a tab after tearing it then closing that specfic window. However , I cant seem to find all the TabItem when ever I try to search for it in the grid of the current winodw I get nothing , this what I treid.
<Window x:Class="TeheMan8_Editor.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:local="clr-namespace:TeheMan8_Editor"
xmlns:dragablz="http://dragablz.net/winfx/xaml/dragablz"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<dragablz:TabablzControl Name="tabHub" AllowDrop="True" Grid.ColumnSpan="2">
<dragablz:TabablzControl.InterTabController>
<dragablz:InterTabController />
</dragablz:TabablzControl.InterTabController>
<TabItem Name="mainTab" Header="Tab No. 1" IsSelected="True">
<Button Click="Button_Click">Open FileButton</Button>
</TabItem>
</dragablz:TabablzControl>
</Grid>
</Window>
CSharp Code:
using DiscUtils.Iso9660;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
namespace TeheMan8_Editor
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var items = this.grid.Children.OfType<TabItem>(); //returns Empty
}
}
}
never mind I figured it out , the TabablzControl Object has a OfType function for the "Items.SouceCollection" property

How to open multiple windows in a queue, each window waiting for previous to close?

I'm trying to create a primitive notifications system.
This is how I show my window:
var data = new NotificationData {
Title = "New order",
OrderId = "1"
}
var notification = new NotificationWindow(data);
Activated += (s, e) => { notification.Owner = this; };
notification.Show();
Then the NotificationWindow just displays provided data. But if there are multiple notifications, they all stack in one spot, I need them to appear after previous is closed.
I tried to use ShowDialog() instead and it did work - but the MainWindow hangs when notification is open. Running it in a different thread using Task.Run() causes notifications not to appear.
You need to register to the closed event and then check if another notification is queued.
The key is the ShowOrQueueNotification which checks if there is a notification present or not. If there is one present, queue the new one and when the notification is closed, you need to check if there is one queued.
I've made an example to show how to use a queue for it:
MainWindow.xaml
<Window x:Class="TestNotificationQueue.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:local="clr-namespace:TestNotificationQueue"
mc:Ignorable="d"
Title="MainWindow" Height="158.868" Width="266.385">
<Grid>
<Border Background="Yellow" VerticalAlignment="Center" HorizontalAlignment="Center" BorderThickness="1" BorderBrush="Black" >
<StackPanel Margin="20">
<TextBox x:Name="TextBoxMessage" Width="192" />
<Button Content="ShowNotification" Width="128" Height="24" Click="Button_Click" Margin="8"/>
</StackPanel>
</Border>
</Grid>
</Window>
MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Windows;
namespace TestNotificationQueue
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private Queue<NotificationInfo> _notificationQueue = new Queue<NotificationInfo>();
private NotificationWindow _currentNotificationWindow;
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
ShowOrQueueNotification(new NotificationInfo(TextBoxMessage.Text));
}
private void ShowOrQueueNotification(NotificationInfo notificationInfo)
{
// -----> If no notification is presented, create one.
if (_currentNotificationWindow == null)
{
_currentNotificationWindow = new NotificationWindow(notificationInfo);
_currentNotificationWindow.Closed += CurrentNotificationWindow_Closed;
_currentNotificationWindow.Show();
}
else
// -----> queue it.
_notificationQueue.Enqueue(notificationInfo);
}
private void CurrentNotificationWindow_Closed(object sender, EventArgs e)
{
// -----> This is crucial, you need to set the current to null, else all new notification will be queued and never be presented.
_currentNotificationWindow = null;
if(_notificationQueue.Count > 0)
ShowOrQueueNotification(_notificationQueue.Dequeue());
}
}
}
Notification.cs
namespace TestNotificationQueue
{
public class NotificationInfo
{
public NotificationInfo(string message)
{
Message = message;
}
public string Message { get; }
}
}
NotificationWindow.xaml
<Window x:Class="TestNotificationQueue.NotificationWindow"
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:TestNotificationQueue"
mc:Ignorable="d"
Title="NotificationWindow" Height="93.117" Width="239.4">
<Grid>
<TextBlock x:Name="TextBoxMessage" HorizontalAlignment="Center" VerticalAlignment="Center" Width="200" Height="30" />
</Grid>
</Window>
NotificationWindow.xaml.cs
using System.Windows;
namespace TestNotificationQueue
{
/// <summary>
/// Interaction logic for NotificationWindow.xaml
/// </summary>
public partial class NotificationWindow : Window
{
public NotificationWindow(NotificationInfo notificationInfo)
{
InitializeComponent();
TextBoxMessage.Text = notificationInfo.Message;
}
}
}

How to trigger a method in a XAML element when a textproperty changes

I really don't know how to search to find a solution to this (I googled a lot, maybe I'm blind...).
I have a ComboBox which also contains a TextBox. The ComboBox is instantiated in a separate Control.xaml with a specific DataContext, where it gets its content for the Popup list.
Now when I type something into the TextBox, I want to trigger a method which then filters the list of my DataContext for the specific elements.
So my ComboBox.cs has some of the following content:
public event EventHandler FilterTextChanged;
protected virtual void OnFilterTextChanged(EventArgs args)
{
FilterTextChanged?.Invoke(FilterText, args);
}
public string FilterText
{
get { return _filterText; }
set
{
//This point is reached when I type something into the TextBox within the ComboBox
if (_filterText == value) return;
_filterText = value;
OnFilterTextChanged(EventArgs.Empty);
OnPropertyChanged("FilterText");
}
}
And in my Control.xaml, I have configured it like this:
<my:ComboBox x:Name="FURecipeComboBox"
AuthorizationMode="IsEnabled"
IsTextSearchEnabled="False"
StaysOpenOnEdit="True"
FilterTextChanged="FURecipeComboBox_OnFilterTextChanged"
ItemsSource="{Binding RecipeFileNames}"
SelectedItem="{Binding Value, Delay=100, ElementName=AlphaNumericTouchpadTextVarIn}"
d:DataContext="{d:DesignInstance {x:Type adapter:ToolRecipeVariableInfo}, IsDesignTimeCreatable=False}">
</my:ComboBox>
But I just can't get it to catch the event "FilterTextChanged", and my method "FURecipeComboBox_OnFilterTextChanged" will not be reached anytime...
I would be really really glad for some hints or help!
Kind regards
BB
Have a look at RoutedEvents at microsoft docs
This is a example post from Stackoverflow
In your case try to change EventHandler to RoutedEventHandler.
I made a small example:
Main.xaml
<Window x:Class="WpfApp1.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:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<local:UserControl1 HorizontalAlignment="Left" Height="207" Margin="348,175,0,0" VerticalAlignment="Top" Width="311" MyClick="UserControl1_MyClick"/>
</Grid>
Main.cs
using System.Windows;
namespace WpfApp1
{
/// <summary>
/// Interaktionslogik für MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void UserControl1_MyClick(object sender, RoutedEventArgs e)
{
MessageBox.Show("Yep");
}
}
}
control.cs
public partial class UserControl1 : UserControl
{
public event RoutedEventHandler MyClick;
public UserControl1()
{
InitializeComponent();
}
private void textBox_KeyDown(object sender, KeyEventArgs e)
{
if(e.Key == Key.Enter)
{
if (MyClick != null)
MyClick(this, new RoutedEventArgs());
}
}
}
control.xaml
<UserControl x:Class="WpfApp1.UserControl1"
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:WpfApp1"
mc:Ignorable="d"
d:DesignHeight="372.313" d:DesignWidth="350">
<Grid>
<TextBox x:Name="textBox" HorizontalAlignment="Left" Height="218" Margin="59,54,0,0" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="202" KeyDown="textBox_KeyDown"/>
</Grid>

Validate Input of UserControl in Window WPF

Currently I have a UserControl contained within a window. The UserControl is made up of two text boxes. The UserControl is an element in my MainWindow. Outside the scope of my UserControl is my submit button in my window. I would like to enable and disable the button whenever the boxes text contents are not null or null.
UserControl XAML code:
<UserControl x:Class="myClass.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"
mc:Ignorable="d">
<StackPanel Background="White">
<DockPanel>
<Label x:Name="lbl1" Content="First Box:"/>
<TextBox x:Name="txtbox1"/>
<Label x:Name="lbl1" Content="Second Box:"/>
<TextBox x:Name="txtbox2"/>
</DockPanel>
</StackPanel>
</UserControl>
View Model:
using System;
namespace myClass {
partial class UserControlViewModel: ViewModelBase {
private bool _validInput;
public UserControlViewModel() {
validInput = false;
}
public object validInput {
get { return _validInput; }
set {
_validInput = value;
OnPropertyChanged("validInput");
}
}
}
ViewModelBase:
using System.ComponentModel;
namespace myClass {
class ViewModelBase : INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName) {
var handler = PropertyChanged;
if (handler != null) {
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
My issue is deciding on how to handle this validation, my button's isEnabled property is currently bounded to the validInput boolean of the view model. However, the contents of the user control are not accessible in my window as I have abstracted it as a separate userControl item (I plan on having different user controls available to be shown in the window).
MainWindow XAML:
<Window x:Class="myClass.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:uc="clr-namespace:myClass"
Title="MainWindow" Height="356" Width="699" ResizeMode="NoResize" WindowStartupLocation="CenterScreen">
<Window.DataContext>
<uc:UserControlViewModel/>
</Window.DataContext>
<Grid>
<UserControl x:Name="usrControl"/>
<Button x:Name="btn" Content="Create" Click="btn_Click" IsEnabled = "{Binding validInput}"/>
</Grid>
</Window>
MainWindow C#:
using System;
using System.Windows;
using System.Windows.Controls;
namespace myClass {
public partial class MainWindow: Window {
UserControlViewModel view;
public MainWindow() {
InitializeComponent();
view = new UserControlViewModel();
DataContext = view;
}
}
I need to be able to check the contents of the text boxes in the UserControl from the MainWindow as my view is in the MainWindow, however the contents are inaccessible to me and it doesn't make sense to have the view in the UserControl. How should I go about solving this?
I've created a similar project. Mainly to do this, validate through your c# code. Basically
(i don't remember id its .content or .text to get the value)
if(txtbox1.Content == ---or--- (textbox1.Content).Equals(Whatever)){
----code---
}
else{
MessageBox.Show("Error")
}
instead of 'disabling' the button (which I don't think you can do) just make it so if invalid, the user knows or just doesn't do anything.
unrelated: if you are wanting a certain input instead of a blank textbox input, you could use this code to give a base if user leaves empty
private void txtbox1_GotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
if (txtbox1.Text.Equals("your origional text"))
{
Name_Text.Text = "";
}
}
private void txtbox1_LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
if (Name_Text.Text.Equals(""))
{
Name_Text.Text = "your origional text";
}
}
hope this helps

Cascaded command bindings possible

I have a MainWindow that contains multiple views using MVVM. In fact the MainWindow holds only a list of Models and each time a Model is added it creates a View (here a UserControl) and associates the DataContext (Model) to the View. Each view is put into a separate TabItem in a TabControl.
The Model itself has a CommandBindingsCollection and assigns this command bindings to the View. This means that for example F10 is available multiple times, but should only the active View should react on F10.
But, F10 does not work at all. Only if I assign the CommandBinding to the MainWindow it works, but this makes the View dependent on the UserControl and this is not what I want, since I wnat to create the View as independent as possible from the MainWindow.
The only solution I have is to make this dynamically intercepting the change of the current TabItem and add/remove the commands for the active View. Currently everything works without code, but then I have to write code for it.
Attached is a code of the MainWindow:
<Window x:Class="WpfApplication3.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:local="clr-namespace:WpfApplication3"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
</Window>
using System.Windows;
namespace WpfApplication3
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
PluginControl aControl = new PluginControl();
Content = aControl;
}
}
}
and the code of the UserControl:
<UserControl x:Class="WpfApplication3.PluginControl"
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:WpfApplication3"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" Background="White">
<StackPanel>
<Button Command="{Binding myCommand}" Content="PushMe" Focusable="False"/>
<Label Content="Nothing pressed" Name="myLabel"/>
</StackPanel>
</UserControl>
using System;
using System.Windows.Controls;
using System.Windows.Input;
namespace WpfApplication3
{
/// <summary>
/// Interaction logic for PluginControl.xaml
/// </summary>
public partial class PluginControl : UserControl
{
public RoutedCommand myCommand
{
get;
private set;
}
public PluginControl()
{
InitializeComponent();
DataContext = this;
myCommand = new RoutedCommand();
myCommand.InputGestures.Add(new KeyGesture(Key.F10));
CommandBindings.Add(new CommandBinding(myCommand, myCommandHandler));
}
private void myCommandHandler(object sender, ExecutedRoutedEventArgs executed)
{
myLabel.Content = DateTime.Now.ToString();
}
}
}
As you see I have set the Button to Focusable false, since I want to have the command working in general, not only when the button is focused. I'm I missing something or thinking in a wrong direction, that making adding a CommandBinding does not implicitly mean that the command does work without binding it?
Adding the Command to the MainWindow itself works.
Any help?

Categories