I have a TemplatedControl and some daughter controls inside it. I want to assign them some events. Initially axaml file cannot access C# module, but when I add x:Class="MyNamespace.MyTemplatedControl" to axaml, although C# code is seen from it, an error occurs:
MyTemplatedControl.axaml(9, 6): [XAMLIL] No Content property or any Add methods found for type MyDesktopApp:MyNamespace.MyTemplatedControl Line 9, position 6.
Looking for solution, all I have found was this variant with Button and its Command property. But in this case I will be restricted only with click event, without opportunity to handle text changed, drag and drop, getting and loosing input focus and other popular actions.
MyTemplatedControl.axaml:
<Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:MyNamespace"
x:Class="MyNamespace.MyTemplatedControl">
<Design.PreviewWith>
<controls:MyTemplatedControl />
</Design.PreviewWith>
<Style Selector="controls|MyTemplatedControl">
<!-- Set Defaults -->
<Setter Property="Template">
<ControlTemplate>
<StackPanel>
<TextBlock
Text="Templated Control"
DoubleTapped="InputElement_OnTapped"/>
<TextBox
KeyDown="InputElement_OnKeyDown"/>
</StackPanel>
</ControlTemplate>
</Setter>
</Style>
</Styles>
MyTmplatedControl.axaml.cs:
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Media;
namespace MyNamespace;
public class MyTemplatedControl : TemplatedControl
{
private void InputElement_OnTapped(object? sender, RoutedEventArgs e)
{
(sender as TextBlock).Text = "DOUBLE TAPPED";
}
private void InputElement_OnKeyDown(object? sender, KeyEventArgs e)
{
(sender as TextBox).Background = new SolidColorBrush(Colors.Green);
}
}
PS. As a beginner, I also don't know yet, how to access controls from .axaml.cs to .axaml, help me, if it is possible, but I think, this is a topic for another issue.
As I'm new in C# WPF, I tried to give it a go, using the trial-and-error approach.
What I would like to do, is to change the appearance of a row in a DataGrid, based on some user action.
However, from reading information over the internet, I've understood that the "right" way to go is:
You should not write a method or a function: you need to write an event.
Okay, okay, so I started doing this in the XAML:
<DataGrid x:Name="dg_Areas" ... LoadingRow="View_dg_Areas"/>
Apparently, the View_dg_Areas event handler should have following signature:
private void View_dg_Areas(object sender, DataGridRowEventArgs e) { }
But then what?
I started, very naïvely, as follows:
private void View_dg_Areas(object sender, DataGridRowEventArgs e)
{
System.Diagnostics.Debug.WriteLine(e.ToString());
System.Diagnostics.Debug.WriteLine(e.Row.ToString());
System.Diagnostics.Debug.WriteLine(e.Row.Item.ToString());
}
The idea was to learn from this how I can find information of the corresponding row (is there a way to read the value of a certain column?), but I didn't get anywhere.
I can tell you that the DataGrid is linked to a DataTable, as follows:
dg_Areas.ItemsSource = dataSet.Tables["Areas"].DefaultView;
How can I link the event parameter e and the DataTable the DataGrid is representing?
You could use a RowStyle with a DataTrigger that binds to one of your properties or columns:
<DataGrid x:Name="dg_Areas">
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Style.Triggers>
<DataTrigger Binding="{Binding YourColumn}" Value="SomeValue">
<Setter Property="Background" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
</DataGrid>
The above sample markup changes the background colour of the rows where "YourColumn" equals "SomeValue".
Using XAML defined styles to change the visual appearance is considered a best practice. Handling events is not.
I've got an issue with a custom WPF button - whenever I click on it, it receives a click event but does not seem to get toggled (does not stay down when I click on another button). I've tried everything I can think of and still can't get it to work.
One weird thing that I have noticed though is that when I put a breakpoint in the MouseDown handler and just hit F5 to continue at that point, the button gets toggled and stays down. This leads me to believe that this is some sort of focus issue?
<ToggleButton Name="ToggleButton" PreviewMouseDown="ToggleButton_MouseDown_1" IsThreeState="False">
<StackPanel Orientation="Vertical">
<Label Content="Single" FontSize="15" FontWeight="Medium"/>
<Label Content="Speaker"/>
</StackPanel>
</ToggleButton>
private void ToggleButton_MouseDown_1(object sender, MouseButtonEventArgs e)
{
ToggleButton.IsChecked = !ToggleButton.IsChecked;
}
private void ToggleButton_MouseDown_1(object sender, MouseButtonEventArgs e)
{
ToggleButton.IsChecked = !ToggleButton.IsChecked;
}
Help? :)
As #Nick said in his comment, just remove your event handler PreviewMouseDown="ToggleButton_MouseDown_1" completely and it should work just fine.
If this is not the case you must have some other code which is causing the issue.
It sounds like you want the functionality of a set of radio buttons, but the look of a bunch of toggle buttons. In cases like these, using the inherited Control.Template property is really handy:
In your page resources, you can add:
<Style TargetType="RadioButton">
<Setter Property="Template">
<Setter.Value>
<ContentTemplate>
<ToggleButton Content="{TemplateBinding Content}" IsChecked="{TemplateBinding IsChecked}"/>
</ContentTemplate>
</Setter.Value>
</Setter>
</Style>
And then in your code:
<RadioButton Content="MyContent" GroupName="MyGroup"/>
This should make an object that looks like a toggle button, but deselects when you select something from the same group, like a RadioButton. You can further modify the ToggleButton in the Template until you get the look that you want.
I have a tab control that looks like the following:
<TabControl x:Name="tabControl1"
Margin="6,42,12,6"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<TabControl.Style>
<Style TargetType="{x:Type TabControl}">
<Setter Property="SelectedIndex" Value="0"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Test.IsStopped}" Value="True">
<Setter Property="SelectedIndex" Value="5"/>
<Setter Property="Background" Value="Red"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TabControl.Style>
<TabItem...>
<TabItem...>
...
</TabControl>
IsStopped is a property on a viewmodel (implements INotifyPropertyChanged, etc.) Now, this all works fine, but there is a catch. I also have some code behind for things like "Next" and "Back" buttons that increment the tab index
private void NextButton_Click(object sender, RoutedEventArgs e)
{
int old = tabControl1.SelectedIndex;
try
{
tabControl1.SelectedIndex++;
}
catch (IndexOutOfRangeException)
{
tabControl1.SelectedIndex = old;
}
}
If this code is called at any point, the DataTrigger no longer updates the SelectedIndex. The background still changes to Red (just there to test the binding). I understand dependency property value precedence, but this is annoying as I don't seem to have control over what is going on. What exactly is going on? As a bonus question, I know some debugging tricks in WPF but how could I ever begin to see why this isn't working?
EDIT 10/8/2013 2:45 EST
Well this is interesting...I have changed the NextButton_Click event to the following in a small attempt to emulate what happens when a tab is clicked:
private void NextButton_Click(object sender, RoutedEventArgs e)
{
int old = tabControl1.SelectedIndex;
try
{
TabItem nextItem = ((TabItem)tabControl1.Items[old + 1]);
nextItem.IsSelected = true;
}
catch (IndexOutOfRangeException)
{
tabControl1.SelectedIndex = old;
}
}
and things work fine. Does anyone know if there a reason why setting the index explicitly shows the aforementioned behavior?
Well for debugging purposes, I'd hook up a handler to the SelectionChanged event and start looking at what could possibly be different from when it is triggered by your trigger versus by your try-catch. Also x:Name your DataTrigger binding, and look at it in the code behind at runtime, and be sure that that binding is the same as you expect it to be at all times (my first suspicion was that something might be changing here).
I have a tooltip for a Label and I want it to stay open until the user
moves the mouse to a different control.
I have tried the following properties on the tooltip:
StaysOpen="True"
and
ToolTipService.ShowDuration = "60000"
But in both cases the tooltip is only displayed for exactly 5 seconds.
Why are these values being ignored?
If you want to set this for just one tooltip, set the duration on the object having the Tooltip, like this:
<Label ToolTipService.ShowDuration="12000" Name="lblShowTooltip" Content="Shows tooltip">
<Label.ToolTip>
<ToolTip>
<TextBlock>Hello world!</TextBlock>
</ToolTip>
</Label.ToolTip>
</Label>
I'd say that this design was chosen because it allows same tooltip with different timeouts on different controls.
If you want this globally for your whole app, see the accepted answer.
Just put this code in initialization section.
ToolTipService.ShowDurationProperty.OverrideMetadata(
typeof(DependencyObject), new FrameworkPropertyMetadata(Int32.MaxValue));
This was also driving me crazy tonight. I created a ToolTip subclass to handle the issue. For me, on .NET 4.0, the ToolTip.StaysOpen property is not "really" stays open.
In the class below, use the new property ToolTipEx.IsReallyOpen, instead of property ToolTip.IsOpen. You will get the control you want. Via the Debug.Print() call, you can watch in the debugger Output window just how many times this.IsOpen = false is called! So much for StaysOpen, or should I say "StaysOpen"? Enjoy.
public class ToolTipEx : ToolTip
{
static ToolTipEx()
{
IsReallyOpenProperty =
DependencyProperty.Register(
"IsReallyOpen",
typeof(bool),
typeof(ToolTipEx),
new FrameworkPropertyMetadata(
defaultValue: false,
flags: FrameworkPropertyMetadataOptions.None,
propertyChangedCallback: StaticOnIsReallyOpenedChanged));
}
public static readonly DependencyProperty IsReallyOpenProperty;
protected static void StaticOnIsReallyOpenedChanged(
DependencyObject o, DependencyPropertyChangedEventArgs e)
{
ToolTipEx self = (ToolTipEx)o;
self.OnIsReallyOpenedChanged((bool)e.OldValue, (bool)e.NewValue);
}
protected void OnIsReallyOpenedChanged(bool oldValue, bool newValue)
{
this.IsOpen = newValue;
}
public bool IsReallyOpen
{
get
{
bool b = (bool)this.GetValue(IsReallyOpenProperty);
return b;
}
set { this.SetValue(IsReallyOpenProperty, value); }
}
protected override void OnClosed(RoutedEventArgs e)
{
System.Diagnostics.Debug.Print(String.Format(
"OnClosed: IsReallyOpen: {0}, StaysOpen: {1}", this.IsReallyOpen, this.StaysOpen));
if (this.IsReallyOpen && this.StaysOpen)
{
e.Handled = true;
// We cannot set this.IsOpen directly here. Instead, send an event asynchronously.
// DispatcherPriority.Send is the highest priority possible.
Dispatcher.CurrentDispatcher.BeginInvoke(
(Action)(() => this.IsOpen = true),
DispatcherPriority.Send);
}
else
{
base.OnClosed(e);
}
}
}
Small rant: Why didn't Microsoft make DependencyProperty properties (getters/setters) virtual so we can accept/reject/adjust changes in subclasses? Or make a virtual OnXYZPropertyChanged for each and every DependencyProperty? Ugh.
---Edit---
My solution above looks weird in the XAML editor -- the tooltip is always showing, blocking some text in Visual Studio!
Here is a better way to solve this problem:
Some XAML:
<!-- Need to add this at top of your XAML file:
xmlns:System="clr-namespace:System;assembly=mscorlib"
-->
<ToolTip StaysOpen="True" Placement="Bottom" HorizontalOffset="10"
ToolTipService.InitialShowDelay="0" ToolTipService.BetweenShowDelay="0"
ToolTipService.ShowDuration="{x:Static Member=System:Int32.MaxValue}"
>This is my tooltip text.</ToolTip>
Some code:
// Alternatively, you can attach an event listener to FrameworkElement.Loaded
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
// Be gentle here: If someone creates a (future) subclass or changes your control template,
// you might not have tooltip anymore.
ToolTip toolTip = this.ToolTip as ToolTip;
if (null != toolTip)
{
// If I don't set this explicitly, placement is strange.
toolTip.PlacementTarget = this;
toolTip.Closed += new RoutedEventHandler(OnToolTipClosed);
}
}
protected void OnToolTipClosed(object sender, RoutedEventArgs e)
{
// You may want to add additional focus-related tests here.
if (this.IsKeyboardFocusWithin)
{
// We cannot set this.IsOpen directly here. Instead, send an event asynchronously.
// DispatcherPriority.Send is the highest priority possible.
Dispatcher.CurrentDispatcher.BeginInvoke(
(Action)delegate
{
// Again: Be gentle when using this.ToolTip.
ToolTip toolTip = this.ToolTip as ToolTip;
if (null != toolTip)
{
toolTip.IsOpen = true;
}
},
DispatcherPriority.Send);
}
}
Conclusion: Something is different about classes ToolTip and ContextMenu. Both have "service" classes, like ToolTipService and ContextMenuService, that manage certain properties, and both use Popup as a "secret" parent control during display. Finally, I noticed ALL the XAML ToolTip examples on the Web do not use class ToolTip directly. Instead, they embed a StackPanel with TextBlocks. Things that make you say: "hmmm..."
You probably want to use Popup instead of Tooltip, since Tooltip assumes that you're using it in the pre-defined UI-standards way.
I'm not sure why StaysOpen doesn't work, but ShowDuration works as documented in MSDN -- it's the amount of time the Tooltip is displayed WHEN it's displayed. Set it to a small amount (e.g. 500 msec) to see the difference.
The trick in your case is maintaining the "last hovered control" state, but once you have that it should be fairly trivial to change the placement target and the content dynamically (either manually, or via binding) if you're using one Popup, or hiding the last visible Popup if you're using multiple.
There are some gotchas with Popups as far as Window resizing and moving (Popups don't move w/the containers), so you may want to also have that in mind while you're tweaking the behavior. See this link for more details.
HTH.
If you want to specify that only certain elements in your Window have
effectively indefinite ToolTip duration you can define a Style in your Window.Resources for those elements. Here is a Style for Button that has such a ToolTip :
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
...>
...
<Window.Resources>
<Style x:Key="ButtonToolTipIndefinate" TargetType="{x:Type Button}">
<Setter Property="ToolTipService.ShowDuration"
Value="{x:Static Member=sys:Int32.MaxValue}"/>
</Style>
...
</Window.Resources>
...
<Button Style="{DynamicResource ButtonToolTipIndefinate}"
ToolTip="This should stay open"/>
<Button ToolTip="This Should disappear after the default time.">
...
One can also add Style.Resources to the Style to change the appearance of the ToolTip it shows, for example:
<Style x:Key="ButtonToolTipTransparentIndefinate" TargetType="{x:Type Button}">
<Style.Resources>
<Style x:Key="{x:Type ToolTip}" TargetType="{x:Type ToolTip}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="HasDropShadow" Value="False"/>
</Style>
</Style.Resources>
<Setter Property="ToolTipService.ShowDuration"
Value="{x:Static Member=sys:Int32.MaxValue}"/>
</Style>
Note: When I did this I also used BasedOn in the Style so everything else defined for the version of my custom control with a normal ToolTip would be applied.
I was wrestling with the WPF Tooltip only the other day. It doesn't seem to be possible to stop it from appearing and disappearing by itself, so in the end I resorted to handling the Opened event. For example, I wanted to stop it from opening unless it had some content, so I handled the Opened event and then did this:
tooltip.IsOpen = (tooltip.Content != null);
It's a hack, but it worked.
Presumably you could similarly handle the Closed event and tell it to open again, thus keeping it visible.
Just for the sake of completeness:
In code it looks like this:
ToolTipService.SetShowDuration(element, 60000);
Also if you ever want to put any other control in your ToolTip, it won't be focusable since a ToolTip itself can get focus. So Like micahtan said, your best shot is a Popup.
Got my issue fixed with the same code.
ToolTipService.ShowDurationProperty.OverrideMetadata(
typeof(DependencyObject), new FrameworkPropertyMetadata(Int32.MaxValue));
(almost) Simple XAML version for ALL tooltips:
Put this style in your window resources (and add the missing types you commonly use)
<Style x:Key="LongToolTipStyle" TargetType="FrameworkElement">
<Setter Property="ToolTipService.ShowDuration" Value="20000"/>
</Style>
<Style TargetType="TextBlock" BasedOn="{StaticResource LongToolTipStyle}"/>
<Style TargetType="Button" BasedOn="{StaticResource LongToolTipStyle}"/>
<Style TargetType="TextBox" BasedOn="{StaticResource LongToolTipStyle}"/>
<Style TargetType="RadioButton" BasedOn="{StaticResource LongToolTipStyle}"/>
<Style TargetType="CheckBox" BasedOn="{StaticResource LongToolTipStyle}"/>
<Style TargetType="ComboBox" BasedOn="{StaticResource LongToolTipStyle}"/>
<Style TargetType="Grid" BasedOn="{StaticResource LongToolTipStyle}"/>
<Style TargetType="StackPanel" BasedOn="{StaticResource LongToolTipStyle}"/>
<Style TargetType="DockPanel" BasedOn="{StaticResource LongToolTipStyle}"/>
<Style TargetType="Image" BasedOn="{StaticResource LongToolTipStyle}"/>
Might be a bit boring, but you can keep it in a resource dictionary.
It's sad that we can't apply these styles to subclasses.
Downside
Whenever you create another style, you must remember to make that style BasedOn="{StaticResource LongToolTipStyle}"
ToolTipService.ShowDurationProperty.OverrideMetadata(
typeof(DependencyObject), new FrameworkPropertyMetadata(Int32.MaxValue));
It is working for me. Copy this line into your class constructor.