I have a Control that is in a WindowsFormsHost. This control is for drawing content in an OpenGL context and need to be invalidated after every draw.
The problem: WPF Controls are not reacting anymore, if I do that. You can click on them and they work, but you do not get any visual feedback like when you hover over it or press the WPF button. The button does not change the color or something it normaly does, if you hover over it.
I created a little repro for you:
XAML
<Grid>
<WindowsFormsHost x:Name="uxContainerWindowsFormsHost" HorizontalAlignment="Left" Height="163" Margin="101,29,0,0" VerticalAlignment="Top" Width="223"/>
<Button Content="Button" HorizontalAlignment="Left" Margin="225,260,0,0" VerticalAlignment="Top" Width="75"/>
</Grid>
Code
public MainWindow()
{
InitializeComponent();
var b = new System.Windows.Forms.Button();
b.BackColor = System.Drawing.Color.Gray;
b.Text = "Button";
b.Paint += b_Paint;
uxContainerWindowsFormsHost.Child = b;
}
void b_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
(sender as System.Windows.Forms.Button).Invalidate();
}
If you click on the WPF button, you will not get any visual feedback that you clicked on the button.
Question
How can I use a continous invalidation in my WindowsFormsHost like in the repro, without loosing the visual feedback on my buttons?
The solution was the CompositionTarget.Rendering event that Dr. ABT showed in the comment.
Constructor()
{
CompositionTarget.Rendering += CompositionTarget_Rendering;
}
void CompositionTarget_Rendering(object sender, EventArgs e)
{
_myControl.Invalidate();
}
Related
I'm trying to get my border focused after the user clicks on it.
Currently it is possible to focus the border via tabs, but via click would be way more convenient for the user.
<Border x:Name="BorderFileInfo" Focusable="True" BorderBrush="LightGray" BorderThickness="1">
<Grid Margin="3,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left">
<!-- CONTENT CTRL -->
</Grid>
</Border>
I saw in another post that there is a possability to catch the click event with an InputBinding but I don't know how to focus the border afterwards without using a command.
Stackoverflow: Why doesnt WPF border control have a mousedoubleclick event?
Is there an easy way to do that other than having to create commands ?
The app is pretty small so I don't want to use commands if I don't have to.
One easy way is to handle PreviewMouseDown or similar mouse events and set the focus:
private void Border_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
Keyboard.Focus(sender as Border);
}
edit
note that you can create Click by handling PreviewMouseLeftButtonDown and PreviewMouseLeftButtonUp in this way:
_isdown =false;
private void Border_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
_isdown =true;
}
private void Border_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if ( _isdown)
{
_isdown = false;
Keyboard.Focus(sender as Border);
}
}
How can I make a tooltip appear when a button is hovered over with the mouse in WPF?
Try this:
<Button ToolTipService.InitialShowDelay="5000"
ToolTipService.ShowDuration="2000"
ToolTipService.BetweenShowDelay="10000"
ToolTip="This is a tool tip." />
"ToolTip" is the property that needs to be set for adding text to controls that are actively being hovered over.
You can create 2 events: PointerEntered and PointerExited, draw content for button and give a name to content (or to it's template)
Xaml:
<Button PointerEntered="Button_PointerEntered" PointerExited="Button_PointerExited" >
<Button.Content>
<TextBlock x:Name="txtBlock1" Text="not hovering" />
</Button.Content>
</Button>
on code behind you handle those events:
private void Button_PointerEntered(object sender, PointerRoutedEventArgs e)
{
txtBlock1.Text = "hovering";
}
private void Button_PointerExited(object sender, PointerRoutedEventArgs e)
{
txtBlock1.Text = "not hovering";
}
I have a Windows Store-style WPF application, and I just added search to it. When I click the Search button in the app bar, I set my FlyoutPresenter containing the SearchBox to Visible. This button is placed in the lower right-hand corner. It works good on computers with keyboards, but I ran into a problem when the virtual keyboard, or InputPane, opens. First, the keyboard covered the box. I solved that problem by checking and adjusting the margin of the box when the box is in focus, but when I scroll the page to the very top and bottom, the control starts moving on the page. Here is my minimal code:
XAML:
<Grid Background="White" x:Name="MainGrid">
<!-- App Bar with Search button -->
<AppBar x:Name="BAppBar" VerticalAlignment="Bottom">
<CommandBar>
<CommandBar.PrimaryCommands>
<AppBarButton Icon="Find" Label="Search" Click="Search_Click"/>
</CommandBar.PrimaryCommands>
</CommandBar>
</AppBar>
<!-- Search button and Close button -->
<FlyoutPresenter VerticalAlignment="Top" Name="SearchPop" Visibility="Collapsed">
<StackPanel Orientation="Horizontal">
<SearchBox Name="Search" GotFocus="Search_Focus" LostFocus="Search_Focus"/>
<AppBarButton Name="SearchClose" Icon="Cancel" Click="Search_Close" />
</StackPanel>
</FlyoutPresenter>
</Grid>
C#:
public partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
// Close app bar, show search box, and set margin to bottom of page
private void Search_Click(object sender, RoutedEventArgs e)
{
BAppBar.IsOpen = false;
SearchPop.Visibility = Windows.UI.Xaml.Visibility.Visible;
SearchPop.Margin = new Thickness(0, MainGrid.ActualHeight - SearchPop.ActualHeight, 0, 0);
}
// Set margin for opening/closing virtual keyboard
private void Search_Focus(object sender, RoutedEventArgs e)
{
Windows.UI.ViewManagement.InputPane.GetForCurrentView().Showing += (s, args) =>
{
double flyoutOffset = (int)args.OccludedRect.Height - SearchPop.ActualHeight;
SearchPop.Margin = new Thickness(0, flyoutOffset, 0, 0);
};
Windows.UI.ViewManagement.InputPane.GetForCurrentView().Hiding += (s, args) =>
{
SearchPop.Margin = new Thickness(0, MainGrid.ActualHeight - SearchPop.ActualHeight, 0, 0);
};
}
// Close search
private void Search_Close(object sender, RoutedEventArgs e)
{
SearchPop.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
}
}
What I need is for the box to not be affected by the user scrolling in the screen. In HTML, this is called Fixed Positioning. I have read that it is not natively possible in XAML, but that there are workarounds. I have read these MSDN and SO links, but they didn't really help:
http://social.msdn.microsoft.com/Forums/en-US/9779328a-a7cd-447d-a4ac-bcc952083f43/fixed-positioning-in-wpf?forum=wpf
http://social.msdn.microsoft.com/Forums/windowsapps/en-US/7349d01d-dc0e-4e1c-9327-df90e00fbebf/how-to-handle-the-appearance-of-the-onscreen-keyboard?forum=winappswithcsharp
Popup control moves with parent
You can simulate the fixed behavior in XAML in a very simple way:
<Grid Background="White" x:Name="MainGrid">
<ContentControl VerticalAligment="Stretch" HorizontalAligment="Stretch">
<!--All other visual controls, the float item will be located over all controls located here, even scrolls viewers-->
</ContentControl>
<!-- Float item -->
<SomeControl>
<!--The control you want be over in the fixed position,
you can set the layout to it, and locate it where you want
just set the Vertical/Horizontal Aligment, margin, height, width-->
</SomeControl>
</Grid>
(Sorry if code sample has some sintax errors, I had write it in the fly)
Also wpf has some controls that are displayed on a layer over all other, this elements are context menus, tooltips and adorners, you also could try them.
I hope this ideas helps.
I learning WPF and build an simple application.
This is my button:
<Button x:Name="btnAddFiles" Content="Add" HorizontalAlignment="Left" Margin="1046,34,0,0" VerticalAlignment="Top"
Width="111" Height="34" FontSize="20" Foreground="{DynamicResource {x:Static SystemColors.ActiveBorderBrushKey}}"
Background="{x:Null}" MouseEnter="btnAddFiles_MouseEnter" BorderBrush="Transparent" />
And this is how it looks like:
http://s27.postimg.org/h0iq4mrrz/image.png
I have changed the button background color to Transparent so the background color the you see is all my application background color.
All i want to do is when the mouse is over the button change the background color to Transparent.
Currently this is the current when mouse is over:
http://s30.postimg.org/x61ssujnx/image.png?noCache=1411485462
So i registered to MouseEnter event:
private void btnAddFiles_MouseEnter(object sender, MouseEventArgs e)
{
//btnAddFiles.Background = // change the color
}
But i can see that btnAddFiles.Background require Brush and nor Color
Any idea hot to change it ?
i couldn't see your pictures but this is how we change back color in wpf:
btnAddFiles.Background = Brushes.Transparent;
and you can use your code in a mouse enter and mouse leave event.
1st Edit
private void btnAddFiles_MouseEnter(object sender, MouseEventArgs e)
{
btnAddFiles.Background = Brushes.Transparent;
}
private void btnAddFiles_MouseLeave(object sender, MouseEventArgs e)
{
btnAddFiles.Background = Brushes.Lime;
}
2nd Edit:
for changing border color and thickness:
button1.BorderBrush = Brushes.Red;
Thickness t = new Thickness(5, 5, 5, 5);
button1.BorderThickness = t;
also change your margin, it is out form. try for example
Margin="50,50,0,0"
let me know if you get your answer.
Okay, I have tried to use a popup to get this to work but there are a ton of reasons why that doesn't appear to be a route I want to take...especially because I've spent the last two hours trying to get it to work and I've deemed it unholier than all hell (this is despite the fact that I have popups in other places in the app that work just fine, but I digress...)
Basically I need only one piece of functionality that doesn't appear to be standard out of the box in WPF...I have to determine when someone clicks on something OTHER than a known UI element (I.E. they click away from something to close it...much like a popup set to StaysOpen = false)
From what I have gathered this is quite an arduous task and I can't seem to find a straight answer on the best way to do this...any ideas SO?
EDIT:
One of the commenters wanted me to post some sample code and re-reading through my question I really don't want to post something that is unrelated (the XY problem). I am posting this question for two reasons:
The onmouseleave event gets fired as soon as the popup opens. This means that if the popup is set to 'StaysOpen="False"' that the popup appears and immediately disappears no matter what. I believe wholeheartedly that this will not be an issue if I create a component that appears using the Visibility attribute to appear and disappear rather than placing it in a popup. The only reason I considered the popup component was because of it's StaysOpen=False functionality, not because it needs to float above everything else
The popup itself feels quite hacky, especially because it needs to fit inside of a parent component in the visual tree. As you can see from the code below, I have gotten the popup to fit inside of it's parent...but I really don't like binding a component's width and height to another component's actual width and height. This is the second reason I would like to avoid using a popup.
As a result, while this question could be 'how can I get the popup to work', the original question still stands: "How can I listen for a on click away event?" I would like to create a component that fits in the visual tree logically, and behaves as the following:
On hover over a component, appear
On leave a component disappear
On click on a component persist appearing
On click away from a component or itself close
I have all of the above handled except for on click away
How about the UIElement.LostFocus-Event? That seems to be the one you need.
I think in this case, you can be useful routed events. There are two types of events: Bubbling, Direct and Tunneling. Attention should be paid to Bubbling and Tunneling. Bubbling events rises up the logical tree and tunneling down. Below is a diagram from here:
So that event up / down the tree, it should be set on each control. Usually, the demonstration bubbling events, apply this example:
XAML
<Window x:Class="DemoRoutedEvents.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" MouseUp="somethingClicked">
<Grid MouseUp="somethingClicked">
<StackPanel MouseUp="somethingClicked" Margin="0,0,10,0">
<Label x:Name="btnClickMe" Content="Click Me!" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" Margin="101,22,0,0" MouseUp="somethingClicked"/>
<CheckBox x:Name="chkhandle" Content="CheckBox" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="241,28,0,0" RenderTransformOrigin="-0.588,1.188"/>
<ListBox x:Name="lstEvents" HorizontalAlignment="Left" Height="604" VerticalAlignment="Top" Width="416" Margin="29,66,0,0"/>
</StackPanel>
</Grid>
</Window>
Code behind
public int eventCounter = 0;
private void somethingClicked(object sender, RoutedEventArgs e)
{
eventCounter++;
String message = "#" + eventCounter.ToString() + ":\r\n" +
" Sender: " + sender.ToString() + ":\r\n" +
" Source: " + e.Source + ":\r\n" +
" Original Source: " + e.OriginalSource;
lstEvents.Items.Add(message);
e.Handled = (bool)chkhandle.IsChecked;
if (e.Handled)
lstEvents.Items.Add("Completed");
}
Output
I tried to optimize this process for multiple panels and components. I have created a attached dependency property IsDebugEvent, which is in the class of EventBehaviours. The principle is simple, we take an event handler and set it for all elements of the type Control (almost all the UIElements it inherits). For panels such as a Grid, StackPanel, WrapPanel, etc, Panel is the base class.
In the handler, we find ListBox and display the name of the panel s the element that caused the event, just for test. The example uses the event PreviewMouseLeftButtonDown (tunneling), because the first fires is an event at the Button.Click, and when it works, it conflicts with the event MouseUp. Quote from here:
ButtonBase inherits from UIElement, a Button will also have access to all of the mouse button events defined for UIElement. Because the Button does something in response to button presses, it swallows the bubbling events (e.g. MouseLeftButtonDown and MouseDown). You can still detect these lower level button press events by adding handlers for the tunneling events (e.g. PreviewMouseLeftButtonDown and PreviewMouseDown).
XAML
<Window x:Class="AwayEventHelp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:AwayEventHelp"
Title="MainWindow" Height="550" Width="525"
WindowStartupLocation="CenterScreen">
<Grid>
<CheckBox Name="DebugCheckBox" Width="100" Height="30"
VerticalAlignment="Top"
Content="Debug event" IsChecked="False"
Checked="DebugCheckBox_Checked" Unchecked="DebugCheckBox_Unchecked" />
<StackPanel Name="LeftStackPanel" Width="150" local:EventBehaviours.IsDebugEvent="False"
HorizontalAlignment="Left" Background="BlanchedAlmond">
<Button Name="LeftButton1" Height="30" Content="LeftButton1" />
<Button Name="LeftButton2" Height="30" Content="LeftButton2" />
<Button Name="LeftButton3" Height="30" Content="LeftButton3" />
<Label Name="JustLabelLeft" Content="JustLabelLeft" Background="Azure" HorizontalContentAlignment="Center" />
</StackPanel>
<StackPanel Name="RightStackPanel" Width="150" local:EventBehaviours.IsDebugEvent="False"
HorizontalAlignment="Right" Background="Azure">
<Button Name="RightButton1" Height="30" Content="RightButton1" />
<Button Name="RightButton2" Height="30" Content="RightButton2" />
<Button Name="RightButton3" Height="30" Content="RightButton3" />
<Label Name="JustLabelRight" Content="JustLabelRight" Background="BlanchedAlmond" HorizontalContentAlignment="Center" />
</StackPanel>
<Grid Name="GridPanel" Width="100" Height="100" local:EventBehaviours.IsDebugEvent="False"
VerticalAlignment="Bottom" Background="CadetBlue">
<Label Name="LabelInGrid" Width="100" Height="50" Content="LabelInGrid" Background="AliceBlue" />
</Grid>
<ListBox Name="EventOutput" Width="180" Height="180" Background="AliceBlue" />
</Grid>
</Window>
Code behind
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void DebugCheckBox_Checked(object sender, RoutedEventArgs e)
{
EventBehaviours.SetIsDebugEvent(LeftStackPanel, true);
EventBehaviours.SetIsDebugEvent(RightStackPanel, true);
EventBehaviours.SetIsDebugEvent(GridPanel, true);
}
private void DebugCheckBox_Unchecked(object sender, RoutedEventArgs e)
{
EventBehaviours.SetIsDebugEvent(LeftStackPanel, false);
EventBehaviours.SetIsDebugEvent(RightStackPanel, false);
EventBehaviours.SetIsDebugEvent(GridPanel, false);
}
}
public class EventBehaviours : DependencyObject
{
#region IsDebugEvent declaration
public static void SetIsDebugEvent(DependencyObject target, bool value)
{
target.SetValue(IsDebugEventProperty, value);
}
public static bool GetIsDebugEvent(DependencyObject DepObject)
{
return (bool)DepObject.GetValue(IsDebugEventProperty);
}
public static readonly DependencyProperty IsDebugEventProperty =
DependencyProperty.RegisterAttached("IsDebugEvent",
typeof(bool),
typeof(EventBehaviours),
new UIPropertyMetadata(false, OnIsDebugEvent));
#endregion
private static void OnIsDebugEvent(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
Panel MyPanel = sender as Panel;
if (e.NewValue is bool && ((bool)e.NewValue == true))
{
MyPanel.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(MyControl_PreviewMouseLeftButtonDown);
if (MyPanel.Children.Count != 0)
{
foreach (Control MyControl in MyPanel.Children)
{
MyControl.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(MyControl_PreviewMouseLeftButtonDown);
}
}
}
else
{
foreach (Control MyControl in MyPanel.Children)
{
MyControl.PreviewMouseLeftButtonDown -= new MouseButtonEventHandler(MyControl_PreviewMouseLeftButtonDown);
}
MyPanel.PreviewMouseLeftButtonDown -= new MouseButtonEventHandler(MyControl_PreviewMouseLeftButtonDown);
}
}
/// <summary>
/// Main handler of PreviewMouseLeftButtonDown event
/// </summary>
private static void MyControl_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
string OutInfo = string.Empty;
if (sender.GetType() == typeof(StackPanel))
{
StackPanel MyStackPanel = sender as StackPanel;
Grid MyGrid = MyStackPanel.Parent as Grid;
OutInfo = "PanelName: " + MyStackPanel.Name;
OutInfoInListBox(MyGrid, OutInfo);
}
else if (sender.GetType() == typeof(Grid))
{
Grid MyGrid = sender as Grid;
Grid MyMainGrid = MyGrid.Parent as Grid;
OutInfo = "PanelName: " + MyGrid.Name;
OutInfoInListBox(MyMainGrid, OutInfo);
}
else
{
Control MyControl = sender as Control;
Panel MyStackPanel = MyControl.Parent as Panel;
Grid MyGrid = MyStackPanel.Parent as Grid;
OutInfo = "ControlName: " + MyControl.Name;
OutInfoInListBox(MyGrid, OutInfo);
}
}
/// <summary>
/// Get ListBox and insert some info
/// </summary>
/// <param name="ParentGrid">Panel, where locate ListBox</param>
/// <param name="info">Just string</param>
private static void OutInfoInListBox(Grid ParentGrid, string info)
{
ListBox MyEventOutput = ParentGrid.FindName("EventOutput") as ListBox;
MyEventOutput.Items.Add(info);
}
}
Output
By clicking on the CheckBox, set a dependency property IsDebugEvent in True, subject thus causing OnIsDebugEvent, where we set the handlers. If you deselect the CheckBox in, then all event handlers deleted.
To set the events immediately on startup, you need to make sure that all the items on the successfully booted. This can be done in the event ContentRendered of Window.