IsManipulationEnabled = true prevents touch toggling a togglebutton - c#

I am looking for a solution to making touch able to handle toggling a toggle button when IsManipulationEnabled = true. I have to keep on IsManipulationEnabled due to underlying 3d map.
Here is the test project I have been using.
<Window x:Class="TestingEventManager.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" IsManipulationEnabled="True">
<StackPanel HorizontalAlignment="Stretch">
<ToggleButton Height="40">
<ToggleButton.Style>
<Style TargetType="ToggleButton">
<Setter Property="Content" Value="OFF"/>
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Content" Value="ON"/>
</Trigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
</ToggleButton>
<ComboBox SelectedIndex="0" Height="40">
<ComboBoxItem>Test 1</ComboBoxItem>
<ComboBoxItem>Test 2</ComboBoxItem>
</ComboBox>
</StackPanel>
I have looked into setting it in the app.xaml style, but setting it just for togglebutton seems to not extend down into the combobox style and it can be easily overridden by another style.
I also would not like to make a custom class because then everyone would need to remember to use this derived class.
Here is a msdn blog article that describes some of these issues with mixing touch
MSDN blog article
And here is an article with someone having a similar problem, but she just extended the button.
MSDN Social Article

Well last night I came up with somewhat of a solution that I am going to roll with in our code. It is not the best solution due to it always turning on ManipulationEnabled for every togglebutton and its manually handling the IsChecked now, but its the only thing I could come up with to always manipulate every togglebutton.
private void EnableTouchDownTogglingOfToggleButton()
{
EventManager.RegisterClassHandler( typeof( ToggleButton ), ToggleButton.LoadedEvent, new RoutedEventHandler( TurnOnManipulaitonEnabled ) );
EventManager.RegisterClassHandler( typeof( ToggleButton ), ToggleButton.TouchDownEvent, new RoutedEventHandler( EnableTouchDownTogglingHandler ) );
}
private void TurnOnManipulaitonEnabled( object sender, RoutedEventArgs e )
{
// need to make sure all toggle buttons behave the same so we always assume IsManipulationEnabled is true
// otherwise it can open then close right after due to it firing again from mousedown being able to go through
ToggleButton toggle = sender as ToggleButton;
if ( toggle != null )
toggle.IsManipulationEnabled = true;
}
private void EnableTouchDownTogglingHandler( object sender, RoutedEventArgs e )
{
ToggleButton toggle = sender as ToggleButton;
if ( toggle != null )
toggle.IsChecked ^= true;
}

Related

Easier way in .net Avalonia to change the background color of the Window's System Top bar?

In dotnet's Avalonia-UI framework.
I'm using a dark UI and I managed to make everything dark as per this example but one thing: the window's System top bar in Windows OS.
I have seen in this issue in github that I can set the property HasSystemDecorations="false" to make it go away, but then I would have to implement myself the top bar with the drag functionality, title, close, maximize, minimize, etc, which is a pain when all I want is to change the background color.
What would be the easier way to make the window top bar change to a dark background color?
If the only way is using HasSystemDecorations then what would be the minimal example to implement the dark top bar with the common funcionality to close/minimize/maximize/drag?
Yes, you have to set HasSystemDecorations="false" and implement your own title bar. I have a basic template on Github for how to do this using version 0.10 and fluent theme.
It is actually quite easy, because Avalonia provides a lot of convenience methods for achieving that.
Overview:
Set
ExtendClientAreaToDecorationsHint="True"
ExtendClientAreaChromeHints="NoChrome"
ExtendClientAreaTitleBarHeightHint="-1"
for the Window and then implement a titlebar. For example the close button could look something like this:
<Button Width="46"
VerticalAlignment="Stretch"
BorderThickness="0"
Name="CloseButton"
ToolTip.Tip="Close">
<Button.Resources>
<CornerRadius x:Key="ControlCornerRadius">0</CornerRadius>
</Button.Resources>
<Button.Styles>
<Style Selector="Button:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="Red"/>
</Style>
<Style Selector="Button:not(:pointerover) /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="Transparent"/>
</Style>
<Style Selector="Button:pointerover > Path">
<Setter Property="Fill" Value="White"/>
</Style>
<Style Selector="Button:not(:pointerover) > Path">
<Setter Property="Fill" Value="{DynamicResource SystemControlForegroundBaseHighBrush}"/>
</Style>
</Button.Styles>
<Path Margin="10,0,10,0"
Stretch="Uniform"
Data="M1169 1024l879 -879l-145 -145l-879 879l-879 -879l-145 145l879 879l-879 879l145 145l879 -879l879 879l145 -145z"></Path>
</Button>
If you set IsHitTestVisible="False" on a control, the window below can be dragged in that area. So wrap your whole titlebar in for example a DockPanel:
<DockPanel Background="Black"
IsHitTestVisible="False"
Name="TitleBarBackground"></DockPanel>
Now you obviously still need to mimic the behaviour of the buttons. This can be done in principal like that (again for a concrete example check out the Github repo above):
minimizeButton = this.FindControl<Button>("MinimizeButton");
maximizeButton = this.FindControl<Button>("MaximizeButton");
maximizeIcon = this.FindControl<Path>("MaximizeIcon");
maximizeToolTip = this.FindControl<ToolTip>("MaximizeToolTip");
closeButton = this.FindControl<Button>("CloseButton");
windowIcon = this.FindControl<Image>("WindowIcon");
minimizeButton.Click += MinimizeWindow;
maximizeButton.Click += MaximizeWindow;
closeButton.Click += CloseWindow;
windowIcon.DoubleTapped += CloseWindow;
private void CloseWindow(object sender, Avalonia.Interactivity.RoutedEventArgs e)
{
Window hostWindow = (Window)this.VisualRoot;
hostWindow.Close();
}
private void MaximizeWindow(object sender, Avalonia.Interactivity.RoutedEventArgs e)
{
Window hostWindow = (Window)this.VisualRoot;
if (hostWindow.WindowState == WindowState.Normal)
{
hostWindow.WindowState = WindowState.Maximized;
}
else
{
hostWindow.WindowState = WindowState.Normal;
}
}
private void MinimizeWindow(object sender, Avalonia.Interactivity.RoutedEventArgs e)
{
Window hostWindow = (Window)this.VisualRoot;
hostWindow.WindowState = WindowState.Minimized;
}
Now the last step is that you need to change the icon of the maximize button depending on the window state. For example if you drag a maximized window, it will automatically become restored down and the icon of the maximize button needs to change. Therefore you need to subscribe to the window state of your host window, which can be done by overriding the Window.HandleWindowStateChanged method or by doing something like this:
private async void SubscribeToWindowState()
{
Window hostWindow = (Window)this.VisualRoot;
while (hostWindow == null)
{
hostWindow = (Window)this.VisualRoot;
await Task.Delay(50);
}
hostWindow.GetObservable(Window.WindowStateProperty).Subscribe(s =>
{
hostWindow.Padding = hostWindow.OffScreenMargin;
if (s != WindowState.Maximized)
{
maximizeIcon.Data = Avalonia.Media.Geometry.Parse("M2048 2048v-2048h-2048v2048h2048zM1843 1843h-1638v-1638h1638v1638z");
maximizeToolTip.Content = "Maximize";
}
if (s == WindowState.Maximized)
{
maximizeIcon.Data = Avalonia.Media.Geometry.Parse("M2048 1638h-410v410h-1638v-1638h410v-410h1638v1638zm-614-1024h-1229v1229h1229v-1229zm409-409h-1229v205h1024v1024h205v-1229z");
maximizeToolTip.Content = "Restore Down";
}
});
}
Actually in the snippet above there is one more detail, which needs some attention. At least on windows, a maximized window is actually bigger than the screen. If you dont want your content to go out of the screens' bounds, you need to add a margin to your main control inside the window. Therefore the Padding of the hostWindow is changed accordingly.
Avalonia provides an IWindowImpl.OffScreenMargin property that describes the margin around the window that is offscreen.
You can directly bind to this property in the window's .axml
<Window xmlns="https://github.com/avaloniaui"
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"
ExtendClientAreaToDecorationsHint="True"
ExtendClientAreaChromeHints="NoChrome"
ExtendClientAreaTitleBarHeightHint="-1"
Padding="{Binding $self.OffScreenMargin}">
There is a way without having to create your own minimize/maximize/close buttons (I only tested it on Windows).
In your MainWindow.axaml:
<Window xmlns="https://github.com/avaloniaui"
...
TransparencyLevelHint="AcrylicBlur"
Background="Transparent"
ExtendClientAreaToDecorationsHint="True"/>
<Grid RowDefinitions="30,*">
<!-- Title bar -->
<Grid ColumnDefinitions="Auto,*" IsHitTestVisible="False" Background="Black">
<Image Grid.Column="0" VerticalAlignment="Center" Source="/Assets/YOUR-PATH-TO-YOUR-APP-ICON-IMAGE" Width="18" Margin="12,0,12,0" ></Image>
<TextBlock Grid.Column="1" VerticalAlignment="Center" FontSize="12" >YOUR-APPLICATION-TITLE-HERE</TextBlock>
</Grid>
<!-- Window content -->
<Your-User-Content-Here Grid.Row="1" Background="#222222" />
</Grid>
Here is the example in the AvaloniaUI documentation.
Here is an example in a real project with a black system bar.

MouseLeftButtonUp does not fire for ScrollViewer in WPF control template

In WPF and C# I am trying to set up a mouse drag scrolling feature for a ScrollViewer contained within a control template for a document viewer. The problem: I have not been able to get the MouseLeftButtonEvent to fire.
It is basically the default DocumentViewer template, with a few features modified. Here is an outline in XAML:
<Style x:Key="DocumentViewerStyle1" BasedOn="{x:Null}" TargetType="{x:Type DocumentViewer}">
<!--...—>
<Setter Property="ContextMenu" Value="{x:Null}" /> <!--So does not mess up right click, if I use that-->
<!--...-->
<ControlTemplate TargetType="{x:Type DocumentViewer}">
<!--...-->
<ScrollViewer x:Name="PART_ContentHost" CanContentScroll="True"
IsHitTestVisible="True" HorizontalScrollBarVisibility="Auto"
Grid.Row="1" Loaded ="OnScrollViewerLoaded" />
<DockPanel Grid.Row="1" >
<!-...-->
</ControlTemplate>
</Style>
I use the following in code behind so that I can access the ScrollViewer. If one changes “Left” to “Right” in the method below, it works to perfection, but of course with the right mouse button rather than the left.
public partial class PrintPreview : Window
{
private ScrollViewer nomad;
etc. and
private void OnScrollViewerLoaded(object sender, RoutedEventArgs e)
{
nomad = (ScrollViewer)sender;
nomad.AddHandler(MouseLeftButtonDownEvent, new MouseButtonEventHandler(OnMouseButtonDown), true);
nomad.AddHandler(MouseLeftButtonUpEvent, new MouseButtonEventHandler(OnMouseButtonUp), true);
nomad.AddHandler(MouseMoveEvent, new MouseEventHandler(OnMouseMove), true);
}
The OnMouseButtonUp event handler, for example, is
private void OnMouseButtonUp(object sender, MouseButtonEventArgs e)
{
nomad.Cursor = Cursors.IBeam;
nomad.ReleaseMouseCapture();
}
Have tried various things found here: No help from using Preview events for my three mouse events. No help from setting Focusable="False" for the ScrollViewer, or for setting a Background for it. Any suggestions? Thanks!

ObservableCollection + ResourceDictionnary + Listbox PropertyChanged does not work

I realize my own Calendar.
I did a Generic.xaml (ResourceDictionnary) which contains my new control. I have a Calendar.class who implement :Control.
In my Calendar class I have a ObservableCollection<Day> _days. I put DataContext = this;
A Day contains my ObservableCollection<MyObject> ListeTache.
And Day.Class Implement INotifyPropertyChanged and have my event :
public event PropertyChangedEventHandler PropertyChanged;
But when I update my Listbox, I have to reload my calendar manually to see any changes.
Am I missing something ?
Thank you for the help.
My ObservableCollection<MyObject> :
public ObservableCollection<Tache> ListeTache
{
get { return this._listeTache; }
set
{
_listeTache = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("ListeTache"));
}
}
}
My Generic.xaml look like this :
<Grid x:Name="LayoutTache">
<ListBox x:Name="ListeTaches" ItemsSource="{Binding ListeTache,UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" FontSize="10" PreviewMouseDown="PreviewMouseDownClick_clear" MouseDoubleClick="doubleClic" IsSynchronizedWithCurrentItem="False">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding AffichageCalendrier}"></TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.Resources>
<ContextMenu x:Key="MonMenu">
<MenuItem Header="Supprimer" Click="MonMenuDel_Click" />
</ContextMenu>
</ListBox.Resources>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="AntiqueWhite"></Setter>
<Setter Property="ContextMenu" Value="{StaticResource MonMenu}"/>
</Trigger>
</Style.Triggers>
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="LightGreen" />
</Style.Resources>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
</Grid>
After somme reply :
How I can do that ? I have to add something like this in my Day.cs class :
_listeTache.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(_listeTache_CollectionChanged);
void _listeTache_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
throw new NotImplementedException();
}
I never catch the event...
Thanks for all
When designing a custom control, it is customary not to set the DataContext to this... in fact, don't set it to anything as this enables it to be set from outside the control. Instead, you should reference your property from generic.xaml using a RelativeSource Binding:
<ListBox x:Name="ListeTaches" ItemsSource="{Binding ListeTache, RelativeSource={
RelativeSource AncestorType={x:Type YourXamlNamespacePrefix:Calendar}}}" ... />
It should also be noted that using UpdateSourceTrigger=PropertyChanged, Mode=TwoWay on an ItemsSource Binding is pointless as the ItemsControl cannot update the source collection.
If you still can't access the property, then you must either ensure that you correctly implement the INotifyPropertyChanged interface in Calendar.cs, or you can implement your ListeTaches property as a DependencyProperty instead.
UPDATE >>>
You've clearly done something wrong... it's really not that complicated. Follow the links that I provided to declare a DependencyProperty in your Calendar.cs class. Do not set the DataContext. Use the RelativeSource Binding that I showed you, correctly setting up the proper XAML Namespace... that's it!
Just one last thing... you did add a WPF Custom Control Library project into your application, didn't you? You need to have something like this in your Calendar class' static constructor:
static Calendar()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(Calendar),
new FrameworkPropertyMetadata(typeof(Calendar)));
}
Perhaps it would help if you read through the Control Authoring Overview page on MSDN to ensure that you are doing it correctly?
UPDATE 2 >>>
Ok, so after noticing something in your comments, I think that I know what your problem is. #XAMlMAX asked you
have you tried removing that null check for your PropertyChanged in Listtache?
You replied
When I remove it, I catch TargetInvocationException.
I think that that's your problem... that means that your PropertyChanged event is null... that means that you have not attached a handler to it yet... it's not being used. Try attaching a handler to it and your ListeTache collection should display fine.

Disable Style in WPF XAML?

Is there anyway to turn off a style programatically?
As an example, I have a style that is linked to all textboxes
<Style TargetType="{x:Type TextBox}">
I would like to add some code to actually stop the style elements being used, so basically reverting back to the default control style.
I need a way to make a switch in my styles, so I can switch between Windows default style and my custom style through C# code.
Is there anyway to do this?
Thanks
Working Solution
Switching between themes in WPF
For setting the style to default,
In XAMl use,
<TextBox Style="{x:Null}" />
In C# use,
myTextBox.Style = null;
If style needs to be set as null for multiple resources, see CodeNaked's response.
I feel, all the additional info should be in your question and not in the comments. Anyways, In code Behind I think this is what you are trying to achieve:
Style myStyle = (Style)Application.Current.Resources["myStyleName"];
public void SetDefaultStyle()
{
if(Application.Current.Resources.Contains(typeof(TextBox)))
Application.Current.Resources.Remove(typeof(TextBox));
Application.Current.Resources.Add(typeof(TextBox),
new Style() { TargetType = typeof(TextBox) });
}
public void SetCustomStyle()
{
if (Application.Current.Resources.Contains(typeof(TextBox)))
Application.Current.Resources.Remove(typeof(TextBox));
Application.Current.Resources.Add(typeof(TextBox),
myStyle);
}
You could inject a blank Style that would take precedence over your other Style. Like so:
<Window>
<Window.Resources>
<Style TargetType="TextBox">
<Setter Property="Background" Value="Red" />
</Style>
</Window.Resources>
<Grid>
<Grid.Resources>
<Style TargetType="TextBox" />
</Grid.Resources>
</Grid>
</Window>
In the example above, only the Grid's implicit Style would be applied to TextBoxes in the Grid. You could even add this to the Grid programmatically, something like:
this.grid.Resources.Add(typeof(TextBox), new Style() { TargetType = typeof(TextBox) });
I know the answer has been accepted, but i want to add my solution which works awesome in the following scenario:
One main application using mahapps.metro
additional project imported from the main application with no reference to mahapps.metro, it is imported as a plugin (loading compiled .dll on the fly)
using the < ToolBar> re-styles everything to null therefore the mahapps.metro styles are not being applied to items inside the toolbar.
usercontrol is used to provide custom controls to the main application.
in the user control root set the resources:
<UserControl.Resources>
<Style x:Key="ButtonStyle" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}" />
<Style x:Key="ComboBoxStyle" TargetType="ComboBox" BasedOn="{StaticResource {x:Type ComboBox}}" />
</UserControl.Resources>
then the toolbar code can be the following
<ToolBar>
Block Template:
<ComboBox Style="{StaticResource ComboBoxStyle}"/>
<Button Content="Generate!" Style="{StaticResource ButtonStyle}"/>
</ToolBar>
this successfully applies the main application style to the controls inside the < ToolBar>
In Xaml, you can override this by setting a style explicitly. In code-behind, you can also set the style explicitly.
<TextBox Style="{StaticResource SomeOtherStyle}"/>
myTextBox.Style = Application.Resources["SomeOtherStyle"];

How to change property of every control in application

what I want to do is to give the user the ability to change some (here one) property of all text boxes in my application. When the user triggers my event, every textbox in every usercontrol, panel etc. is ought to be changed.
For Example all textboxes should change to Multiline=true; (I know this doesn't make much sense, but my needs a really similar to this), but how to achieve this without looping over every control?
I know I could do something like
foreach(Control item in FindForm().Controls)
{
if(item is TextBox)
{
(item as TextBox).Multiline=true;
}
}
but I don't think that this is a perfect nor a good solution.
I know I could write the settings to a file and read them when the app is starting, but how to change the properties while running the application?
My main problem know is that the ControlProperties don't let me give them a reference to a boolean object, so I can't easily change it in a "settings-object", or do I miss here something?
I don't know of any good tutorials to walk you through it but you can do a DataBinding to any property (including Multiline) not just the text one. This should do what you need to do.
this.txtField.DataBindings.Add(
new System.Windows.Forms.Binding("Multiline",
global::ProjectNamespace.Properties.Settings.Default,
"BoolianSettingInConfigFile",
true,
System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
I used the config file in this example but it can be stored anywhere.
In these circumstances you have two basic options: push the changes to the control (as you sample code does) or have the controls themselves pull the data from the configuration.
Using a pull approach will allow you to update controls at runtime.
You could use databinding on all of your textboxes to bind the 'Multiline' property of the textboxes to some central store of the setting. You could go further and derive custom textbox controls that automatically handle their own databinding setup on instantiation, so once you have replaced the textboxes with your own textbox type (this can actually be done with a search and replace in the code) you don't have to make any more code changes.
Maybe you could just use a data trigger and a globally accessible utility class:
Here's an example where at the click of a button all textblock's will have a red foreground
<Window x:Class="RoomUnit.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:RoomUnit" Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<c:Utility x:Key="utility" />
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding Source={StaticResource utility},
Path=IsRed}" Value="true">
<Setter Property="Foreground" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Text="TEST" />
<Button Content="Make Red" Grid.Row="1" Click="MakeRed" />
</Grid>
heres the utility class
public class Utility : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool isRed;
public bool IsRed
{
get { return isRed; }
set
{
isRed = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("IsRed"));
}
}
}
and heres my button eventhandler
private void MakeRed(object sender, RoutedEventArgs e)
{
var u = (Utility) this.FindResource("utility");
u.IsRed = true;
}

Categories

Resources