Xaml and Events not Working - c#

so I just created a basic canvas that has a event. Yet when I run this code the event is never actually hit. Im writing in c# for metro apps. What did I do wrong?
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Canvas HorizontalAlignment="Left" Height="673" VerticalAlignment="Top" Width="1346" Margin="10,85,0,0" PointerMoved="Canvas_PointerMoved"/>
</Grid>
Heres my c# code
public MainPage()
{
this.InitializeComponent();
}
/// <summary>
/// Invoked when this page is about to be displayed in a Frame.
/// </summary>
/// <param name="e">Event data that describes how this page was reached. The Parameter
/// property is typically used to configure the page.</param>
protected override void OnNavigatedTo(NavigationEventArgs e)
{
}
private void Canvas_PointerMoved(object sender, PointerRoutedEventArgs e)
{
Debug.WriteLine("hit");
}

You've discovered a confusing quirk of Canvases. You have to set a background color in order for it to be hit tested.
So, for example, change your code to this and it will hit the event:
<Grid>
<Canvas Background="Blue" HorizontalAlignment="Left" Height="673" VerticalAlignment="Top" Width="1346" Margin="10,85,0,0" PointerMove="Canvas_PointerMoved"/>
</Grid>
But, one thing you'll need to consider is whether Canvas is the right kind of panel to use. It is extremely primitive and generally not used unless you need to rigidly define the layout or are micro-optimizing for performance.

Related

ToggleSwitch in UWP C#

I have a little question. I'm trying to create an UWP C# application, and I'm using toggle switches in the project. If I toggle the status of the ToggleSwitch from the software very often, the memory usage increases very much. Why is this happening?
The ToggleSwitch represents a boolean using binding.
I have created example code:
XAML:
<Page
x:Class="App1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App1"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ToggleSwitch Header="Test" HorizontalAlignment="Left" Margin="213,27,0,0" VerticalAlignment="Top" IsOn="{Binding Path=TestBool, Mode=TwoWay}"/>
<CheckBox Content="Two-state CheckBox" Margin="108,162,0,806" IsChecked="{Binding Path=TestBool, Mode=TwoWay}"/>
<Button Content="Start!" HorizontalAlignment="Left" Margin="69,58,0,0" VerticalAlignment="Top" Click="Button_Click"/>
</Grid>
C#:
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using System.ComponentModel;
using System.Runtime.CompilerServices;
// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409
namespace App1
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
///
public sealed partial class MainPage : Page, INotifyPropertyChanged
{
private bool testBool = false;
public bool TestBool { get { return testBool; } set { if (testBool != value) { testBool = value; OnPropertyChanged(); } } }
public MainPage()
{
DataContext = this;
this.InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
for (int i = 0; i < 10000; i++)
{
TestBool = !TestBool;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Do I do something that is not possible, what do I understand wrong?
When I replace the ToggleSwitch with a CheckBox, the memory usage increases much less.
It seems that a memory leak is occurring on your application, explaining why the GC failed to free it up.
Some considerations:
Why are you implementing INotifyPropertyChanged on the page level? You should implement it on a separate class. That class should hold the model and implement the INotifyPropertyChanged interface itself.
That class is commonly denominated as a ViewModel, and as the name suggests holds the model for a View and it knows how to communicate any changes onto it.
Generally most of the problems with memory leaks with Bindings, that have been reported happen generally when utilizing compiled bindings, {x:Bind ...}, but you are utilizing the traditional bindings. You've correctly performed binding onto a dependency property, such as IsOn and also implemented the INotifyPropertyChanged on the source object, so I don't think that exists something inherently wrong with how you are spinning up the binding process.
You are performing 100 bindings update in a row, so even if the CPU usage should be a bit lower, it is expected that while you are iterating over the loop you will have your CPU usage increased.
Ps: Since you have not marked your method as `async, you will be blocking the UI thread until your loop operation ends, which might be something that you could want to change.
A binding update, is certainly a process with a couple of actions in the middle for each that's why I'm "confident" that the CPU usage might not be a big issue.
I don't actually have a guess, but I would strongly suggest to refactor your code to have the ViewModel class implemented as an extra class, and observe if the memory leaks observed seize to exist.
Looks like your invoke is draining CPU.
My answer is you should think again about your app's architecture.

Set language of the DocumentViewer to German (from code, not XAML)

I am trying to change the language of the DocumentViewer from default English to German but with no success.
Being new to WPF, I really struggle to do this.
IMPORTANT: DocumentViewer is created in code behind, in response to the menu item click, and then it is added as main window's Content.
I have tried doing the following, but it seems to do nothing:
myDocumentViewer.Language = System.Windows.Markup.XmlLanguage.GetLanguage("de-DE");
No changes are made, DocumentViewer keeps English.
Googling for proper usage of the Language property, I found nothing useful.
QUESTION:
How can I set the language of the DocumentViewer (created with code) to German?
What you are trying to accomplish can be done, but not very easily.
I'll start by pointing out that your test machine needs to have the appropriate language resources installed to permit DocumentViewer to show you tooltips etc. in German. In practice, this means that you'll need to have German (Germany) language pack installed on your computer. See Language Packs for details.
What comes below is what I know to the best of my understanding:
WPF does not quite have an in-built infrastructure, as far as I can tell, to dynamically adapt to changes in either Thread.CurrentThread.CurrentUILanguage or to changes in xml:lang(which is equivalent to FrameworkElement.Language property.
WPF controls primarily utilize xml:lang to determine their UI language (assuming that the corresponding UI resources are available), and it is up to the application developer to hook that up with Thread.CurrentThread.CurrentUILanguage if so desired. This in itself is not very hard to do using data-binding, like this:
<DocumentViewer Language="{Binding UILanguage, ConverterCulture={x:Static glob:CultureInfo.InvariantCulture}}" />
That still does not mean that the control thus data-bound would adapt its UI language to changes in Thread.CurrentThread.CurrentUILanguage. Every time you need the UI language to be changed, you need to recreate the control, remove the old control from the visual tree, and add the new one. Roughly, the code would look somewhat like this:
private void ChangeCulture()
{
string ietfLanguageTag = "de-DE";
var cultureInfo = CultureInfo.GetCultureInfo(ietfLanguageTag);
Thread.CurrentThread.CurrentCulture = cultureInfo;
Thread.CurrentThread.CurrentUICulture = cultureInfo;
UILanguage = ietfLanguageTag;
var parent = VisualTreeHelper.GetParent(_documentViewer) as Grid;
int index = parent.Children.IndexOf(_documentViewer);
parent.Children.Remove(_documentViewer);
_documentViewer = new DocumentViewer();
parent.Children.Add(_documentViewer);
}
The above snippet assumes that the visual parent of the DocumentViewer is a Grid, and it is backed by the variable _documentViewer.
Generally, the above solution is too simplistic and is not well suited for MVVM scenarios (which is often the case in WPF applications). You might have data bindings to the DocumentViewer instance, and creating new instances would require that those bindings be recreated in code (if, on the other hand, there happen to be no data-bindings involved, and all settings are set in code, then the above approach would just work, I think).
You can further improve this by creating a simple user control that encapsulates a DocumentViewer, along with any interesting bindings you might wish to preserve. Your control would look like this:
XAML:
<UserControl x:Class="LocalizedDocumentViewer.CultureAwareDocumentViewer"
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:glob="clr-namespace:System.Globalization;assembly=mscorlib"
xmlns:local="clr-namespace:LocalizedDocumentViewer"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="300"
d:DesignWidth="300"
mc:Ignorable="d">
<Grid>
<DocumentViewer DataContext="{Binding}" Language="{Binding UILanguage, ConverterCulture={x:Static glob:CultureInfo.InvariantCulture}}" />
</Grid>
XAML.cs
using System.Windows.Controls;
namespace LocalizedDocumentViewer
{
public partial class CultureAwareDocumentViewer : UserControl
{
public CultureAwareDocumentViewer()
{
InitializeComponent();
}
}
}
Now, you can easily include this in your main application UI, like shown below. The XAML below includes a couple of additional UI elements (buttons and labels) that would help show a complete example:
MainWindow XAML:
<Window x:Class="DocViewerLoc.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:local="clr-namespace:DocViewerLoc"
xmlns:localizedDocumentViewer="clr-namespace:LocalizedDocumentViewer;assembly=LocalizedDocumentViewer"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="DocumentViewer Culture Change Demo"
Width="525"
Height="350"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
mc:Ignorable="d">
<Grid>
<!-- Row and Column Definitions -->
<!-- Define a small row on the top of the window to place buttons -->
<Grid.RowDefinitions>
<RowDefinition Height="25" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<!-- Controls -->
<Button Grid.Row="0"
Grid.Column="0"
Command="{Binding CultureChanger}"
CommandParameter="{Binding RelativeSource={RelativeSource Self},
Path=Content}">
en-us
</Button>
<Button Grid.Row="0"
Grid.Column="1"
Command="{Binding CultureChanger}"
CommandParameter="{Binding RelativeSource={RelativeSource Self},
Path=Content}">
de-DE
</Button>
<Label Grid.Row="0" Grid.Column="2"><-- Click on one of these buttons to change UI culture</Label>
<Grid Grid.Row="1" Grid.ColumnSpan="3">
<localizedDocumentViewer:CultureAwareDocumentViewer x:Name="_documentViewer" DataContext="{Binding}" />
</Grid>
</Grid>
The corresponding code-behind has a couple of dependency properties used to help communicate with the bindings in the above XAML.
MainWindow.xaml.cs
using System;
using System.Globalization;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
namespace DocViewerLoc
{
public partial class MainWindow : Window
{
public MainWindow()
{
CultureChanger = new SimpleCommand(ChangeCulture);
InitializeComponent();
}
/// <summary>
/// ChangeCulture is called when one of the buttons with caption
/// 'en-us' or 'de-DE' is pressed.
/// </summary>
/// <param name="parameter">
/// A string containing the caption 'en-us' or 'de-DE'.
/// </param>
private void ChangeCulture(object parameter)
{
string ietfLanguageTag = parameter as string;
if (ietfLanguageTag == null) return;
var cultureInfo = CultureInfo.GetCultureInfo(ietfLanguageTag);
Thread.CurrentThread.CurrentCulture = cultureInfo;
Thread.CurrentThread.CurrentUICulture = cultureInfo;
// This will ensure that CultureAwareDocumentViewer's Language property
// binds to the updated value set here when it is instantiated next.
UILanguage = ietfLanguageTag;
// Remove the old instance of _documentViewer from the UI.
var parent = VisualTreeHelper.GetParent(_documentViewer) as Grid;
int index = parent.Children.IndexOf(_documentViewer);
parent.Children.Remove(_documentViewer);
// Create a new instance of CultureAwareDocumentViewer. This will
// use the updated value of UILanguage bind it to its Language (xml:lang)
// property, thus resulting in the appropriate language resources being
// loaded.
_documentViewer = new LocalizedDocumentViewer.CultureAwareDocumentViewer();
// Now, add the _documentViewer instance back to the UI tree.
parent.Children.Add(_documentViewer);
}
/// <summary>
/// ICommand used to bind to en-us and de-DE buttons in the UI
/// </summary>
#region CultureChange
public SimpleCommand CultureChanger
{
get { return (SimpleCommand)GetValue(CultureChangerProperty); }
set { SetValue(CultureChangerProperty, value); }
}
// Using a DependencyProperty as the backing store for CultureChanger. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CultureChangerProperty =
DependencyProperty.Register("CultureChanger", typeof(SimpleCommand), typeof(MainWindow), new PropertyMetadata(default(SimpleCommand)));
#endregion
/// <summary>
/// UILanguage property used to bind to the FrameworkElement.Language (xml:lang) property
/// in the DocumentViewer object within the CultureAwareDocumentViewer control.
/// </summary>
#region UILanguage
public string UILanguage
{
get { return (string)GetValue(UILanguageProperty); }
set { SetValue(UILanguageProperty, value); }
}
// Using a DependencyProperty as the backing store for UILanguage. This enables animation, styling, binding, etc...
public static readonly DependencyProperty UILanguageProperty =
DependencyProperty.Register("UILanguage", typeof(string), typeof(MainWindow), new PropertyMetadata(Thread.CurrentThread.CurrentUICulture.IetfLanguageTag));
#endregion
}
/// <summary>
/// Simple implementation of the ICommand interface that delegates
/// Execute() to an Action<object>.
/// </summary>
public class SimpleCommand : ICommand
{
#pragma warning disable 67
public event EventHandler CanExecuteChanged;
#pragma warning restore 67
public SimpleCommand(Action<object> handler)
{
_handler = handler;
}
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
_handler?.Invoke(parameter);
}
private Action<object> _handler;
}
}
The below screenshot show the resulting application UI. Note that the resources in DocumentViewer would switch between English and German, but the rest of the UI would not (because we did not try to localize our application!).
Application showing en-us resources in DocumentViewer:
Application showing de-DE resources in DocumentViewer:
AFAIK you are setting it correctly.
I do not have experience with DocumentViewer, but setting CurrentUICulture does not translate. Setting CurrentUICulture selects between resources that you have in your application for different languages. See https://stackoverflow.com/a/1142840/5569663 for an example. I assume that Language of a DocumentViewer is the same.
Unluckily setting the Language property of your DocumentViewer won't work. The reason of this issue is related to the PresentationUI assembly (it is a standard one), which contains resources which affect DocumentViewer.
The main point is that tooltips and labels are hardcoded in these resources and they are only in english (at least considering .NET 4).
For example, this is how the DocumentViewer's print button is defined (in the themes/generic.baml resource):
<Button Name="PrintButton" x:Uid="Button_14" ToolTip="Print (Ctrl+P)" ToolTipService.ShowOnDisabled="True" Width="24px" Padding="2,2,2,2" Margin="2,2,2,2" VerticalAlignment="Center" Command="ApplicationCommands.Print" IsTabStop="True" TabIndex="0" Style="{DynamicResource {ComponentResourceKey TypeInTargetAssembly={x:Type ui:PresentationUIStyleResources}, ResourceId=PUIDocumentViewerButtonStyle}}" Background="{DynamicResource {ComponentResourceKey TypeInTargetAssembly={x:Type ui:PresentationUIStyleResources}, ResourceId=PUIDocumentViewerPrintButton}}" CommandTarget="{Binding Path=TemplatedParent, RelativeSource={RelativeSource TemplatedParent}}" />
As you can see is defined as "Print (Ctrl+P)". You will find the same situation for other labels which you would localize. Even if x:Uid properties are defined LocBaml won't work since PresentationUI is not a satellite assembly.
So a first solution could be: write your own DocumentViewer style and you can use the language that you prefer. The problem is that inside a DocumentViewer there is a control named FindToolBar. It is declared as internal, so probably it would be hard to redefine its style.
Then I propose an alternative solution to you. My idea is based on the fact that localizable children of a DocumentViewer have a name (you can use ILSpy to establish it).
So you need just to extend DocumentViewer in this way:
public class LocalizedDocumentViewer : DocumentViewer
{
public LocalizedDocumentViewer()
{
Loaded += new RoutedEventHandler(OnLoaded);
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
Button button = FindChild<Button>(this, "PrintButton");
button.ToolTip = Properties.Resources.PrintToolTip;
button = FindChild<Button>(this, "CopyButton");
button.ToolTip = Properties.Resources.CopyToolTip;
button = FindChild<Button>(this, "FindPreviousButton");
button.ToolTip = Properties.Resources.FindPreviousToolTip;
button = FindChild<Button>(this, "FindNextButton");
button.ToolTip = Properties.Resources.FindNextToolTip;
Label label = FindChild<Label>(this, "FindTextLabel");
label.Content = Properties.Resources.FindTextLabel;
/* and so on... */
}
public static T FindChild<T>(DependencyObject parent, string childName)
where T : DependencyObject
{
/* see the link for the code */
}
}
You can find the code of FindChild method here (take a look to CrimsonX's answer).
I know, it is a unelegant solution. I do not like it too. But I guess it is fast and it allows you to preserve the default style look.

Removing the reference to a user control in wpf

I have a wpf application which has a main window and menu. This main window has a panel, and on clicking the menu item i create an instance of the user control and load the panel with the control.
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="" MinHeight="750" Height="Auto" MinWidth="1100" Width="Auto" WindowState="Maximized" ScrollViewer.VerticalScrollBarVisibility="Auto"
Loaded ="MainWindow_OnLoaded" Closing="Window_Closing">
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility ="Auto" SizeChanged="ScrollViewer_SizeChanged">
<Grid Width="Auto">
<Grid.RowDefinitions>
<RowDefinition Height="38"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<StackPanel Height="38" Width="Auto" Background="#09527B">
<Grid Margin="0,0,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition Width="70"></ColumnDefinition>
</Grid.ColumnDefinitions>
</Grid>
</StackPanel>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="189"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
<StackPanel>
<Expander Name="test" Header="Admin" Foreground="White" Margin="0,10,0,0">
<StackPanel Margin="20,0,0,0">
<Expander Header="Data" Foreground="White">
<StackPanel>
<TextBlock Text="Add/Edit UC1" Foreground="White" Margin="30,5,0,0" MouseDown="OpenUC1_MouseDown" MouseEnter="TextBlock_MouseEnter" MouseLeave="TextBlock_MouseLeave"/>
<TextBlock Text="Add/Edit UC2" Name="tbxBuild" Foreground="White" Margin="30,5,0,0" MouseDown="OpenUC2_MouseDown" MouseEnter="TextBlock_MouseEnter" MouseLeave="TextBlock_MouseLeave"/>
</StackPanel>
</Expander>
</StackPanel>
</Grid>
<StackPanel Grid.Column="1">
<Grid Name="pnlMain" Height ="Auto" VerticalAlignment="Top" HorizontalAlignment="Left">
</Grid>
</StackPanel>
</Grid>
</Grid>
</ScrollViewer>
</Window>
MainWindow.cs
private void OpenUC1_MouseDown(object sender, MouseButtonEventArgs e)
{
for (int i = 0; i < pnlMain.Children.Count; i++ )
{
pnlMain.Children.Remove(pnlMain.Children[i]);
}
using (UC2 _uc2= new UC2())
{
pnlMain.Children.Add(_uc2);
}
}
private void OpenUC2_MouseDown(object sender, MouseButtonEventArgs e)
{
for (int i = 0; i < pnlMain.Children.Count; i++ )
{
pnlMain.Children.Remove(pnlMain.Children[i]);
}
using (UC1 _uc1= new UC1())
{
pnlMain.Children.Add(_uc1);
}
}
My question is when I remove the control(UC1) from the main panel, when will that control be disposed?
Both the user control(UC1 and UC2) has the same view model attached to its data context. So i find that some of the methods in the removed user control(UC1) is called even though that is removed from the panel. The reason being, when a new instance of UC2 is created, there are some changes in the data model which in effect calls the dependent methods in UC1.
But if UC1 had been disposed this wouldn't happen. How can I make sure UC1 is disposed before instance of UC2 is created?
public UC1()
{
InitializeComponent();
this.DataContext = App.ViewModel.TestViewModel;
}
private void UC1_Unloaded(object sender, RoutedEventArgs e)
{
this.DataContext = null;
}
public UC2()
{
InitializeComponent();
this.DataContext = App.ViewModel.TestViewModel;
}
private void UC2_Unloaded(object sender, RoutedEventArgs e)
{
this.DataContext = null;
}
The unloaded method is not called immediately when the control is removed from the panel.
When I write and test code to dynamically add and remove a UserControl object from a window's visual tree, I find that the Unloaded event is raised just as expected.
In your own code example, there is at least one serious problem, and two incongruities:
The serious problem is how you are removing children. Your for loop is iterating by index through the children of the pnlMain object (a Grid). But removing any child invalidates the sequence of indexes! That is, the loop will first remove the child at index 0; this causes the child at index 1 to now become the child at index 0. But the loop increments the index before continuing, and will next remove the child at index 1. This child was originally at index 2. The code skips every other child (i.e. the ones originally at odd-numbered indexes), leaving half of them attached as children of the Grid.
Incongruity #1: I would expect a method with the phrase "OpenUC1" in the name to add an instance of UC1. However, your OpenUC1_MouseDown() method seems to be adding an instance of UC2 (and vice a versa for OpenUC2_MouseDown()). At the very least, there should be a comment in the code explaining why the code is different from what one might expect given the name of the method.
Incongruity #2: there is a using statement around the call to Add() when adding the UserControl objects. First, UserControl itself does not implement IDisposable, so unless your types have implemented that interface, that code is not even legal. Second, even if your UserControl subclasses do implement that interface, it does not seem like a very good idea to me to dispose an object that you've just created and which you are retaining in the visual tree (i.e. by adding it to the Grid's children).
Unfortunately, as I mentioned in my comment, without a good, minimal, complete code example that reliably reproduces your problem, it is impossible to say why your code does not behave as one would hope and/or expect it to. It is possible that any of the above points (but especially #1) are the cause of the behavior you're seeing, but I have no way to know for sure.
If after addressing those issues (or determining somehow that they are not problems…though if you can legitimately do that, I would argue that the code is still defective, in the sense that it's poor design), you find that your problem still exists, please edit your question so that it includes a good, minimal, complete code example that reliably reproduces the problem.
In the meantime, here is a simple code example that illustrates the basic behavior of the Unloaded event being raised just as expected when the object is removed from the visual tree. Note that while the correct way to remove all children from the Grid object's Children collection is to simply call the Clear() method (e.g. pnlMain.Children.Clear()), I have included an example of a explicit loop-based approach that does work.
XAML:
UserControl1.xaml
<UserControl x:Class="TestSO33289488UserControlUnloaded.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"
mc:Ignorable="d"
Unloaded="UserControl_Unloaded"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TextBlock Text="UserControl" FontSize="36"/>
</Grid>
</UserControl>
MainWindow.xaml
<Window x:Class="TestSO33289488UserControlUnloaded.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">
<StackPanel>
<Button x:Name="button1" Content="Add UserControl"
HorizontalAlignment="Left" Click="Button_Click"/>
<Grid x:Name="grid1"/>
</StackPanel>
</Window>
C#:
UserControl1.xaml.cs
using System.Windows;
using System.Windows.Controls;
namespace TestSO33289488UserControlUnloaded
{
/// <summary>
/// Interaction logic for UserControl1.xaml
/// </summary>
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
private void UserControl_Unloaded(object sender, RoutedEventArgs e)
{
MessageBox.Show("UserControl.Unloaded was raised");
}
}
}
MainWindow.xaml.cs
using System.Windows;
namespace TestSO33289488UserControlUnloaded
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private bool _removeUserControl;
private void Button_Click(object sender, RoutedEventArgs e)
{
if (_removeUserControl)
{
//grid1.Children.Clear();
// Calling Clear() is better, but if you really want to loop,
// it is possible to do correctly. For example:
while (grid1.Children.Count > 0)
{
grid1.Children.RemoveAt(grid1.Children.Count - 1);
}
button1.Content = "Add UserControl";
}
else
{
grid1.Children.Add(new UserControl1());
button1.Content = "Remove UserControl";
}
_removeUserControl = !_removeUserControl;
}
}
}
Quote from an MSDN forum entry about Loaded/Unloaded events:
The events are raised asynchronously, so there might be some delay
between the action that causes the event and the event itself. The
events are effectively put into a list and a task is added to the
dispatcher's queue. When that task runs, it raises the events on the
list.
So the answer is you can't predict when exactly these events will raised and you shouldn't expect that they will be called immediately after you removed a control from it's parent.
It's kinda difficult to give you a proper solution without seeing the full project, but here's a quick and dirty solution: rather than making sure that the given user controls' events are fired in time let's check the Parent property of the UC1/UC2 object before running the method. If the property is null then the UC1/UC2 object was removed and you should not execute that method.
But let me point out some problems with this code:
What's the point of the using block in the MouseDown event handlers? You create a user control object, add it to the panel and then immediately after that you call the Dispose method on it? (that's what the using block does in C#)
You don't need a for loop to remove all the children elements from a Panel control like a Grid. You can do that in one line. pnlMain.Children.Clear();

How to set transparent background for richtextbox using the WPF RichTextBoxToolBar Control?

I am using the WPF RichTextBoxToolBar control from this Code Project project, combined with the Exteneded WPF Toolkit's RichTextBox control.
When I enter text for the first time with no formatting it's all ok and the background is Transparent. But when I use the toolbar to set the background to Transparent it actually sets it to White. I've noticed the additional \highlight1 parameter in the RTF format of the text which does this thing.
Does anyone have experience with this toolbar? Is there a way to change this behavior? Can I disable the background color picker command?
Following is the code of a small test project I made to investigate this. The project only needs to install the Extended WPF Toolkit from NuGet and reference the dll from this zip.
XAML:
<Window x:Class="RtbTest.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:rtb="clr-namespace:RichTextBoxToolBar;assembly=RichTextBoxToolBar"
xmlns:toolkit="http://schemas.xceed.com/wpf/xaml/toolkit" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<DockPanel>
<rtb:RichTextBoxToolBar Margin="0,0,0,5" DockPanel.Dock="Top" Name="EditTextToolbar" />
<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal">
<Button Content="Save RTF text" Click="ShowRtfText"/>
<Button Content="Reload RTF text" Click="ReloadRtfText"/>
</StackPanel>
<toolkit:RichTextBox Name="EditTextRtb">
<toolkit:RichTextBox.TextFormatter>
<toolkit:RtfFormatter />
</toolkit:RichTextBox.TextFormatter>
</toolkit:RichTextBox>
</DockPanel>
</Window>
Codebehind:
namespace RtbTest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void ShowRtfText(object sender, RoutedEventArgs e)
{
MessageBox.Show(EditTextRtb.Text);
}
private void ReloadRtfText(object sender, RoutedEventArgs e)
{
string rtfText;
rtfText = EditTextRtb.Text;
EditTextRtb.Text = rtfText;
}
}
}

WinRT FlipView binding fails possibly

I have a xaml page:
<Page x:Class="DailyStyleW8.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:DailyStyleW8"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:converters="using:DataTypes"
mc:Ignorable="d">
<Page.Resources>
<converters:PortableImageConverter x:Key="ImageConverter" />
</Page.Resources>
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Grid>
<ProgressBar x:Name="loadingViewer"
IsIndeterminate="True"
Height="20" />
<FlipView x:Name="displayViewer"
ItemsSource="{Binding}"
Visibility="Collapsed">
<FlipView.ItemTemplate>
<DataTemplate>
<Grid>
<Image Source="{Binding Image,Converter={StaticResource ImageConverter}}" />
<TextBlock Text="{Binding Name}" />
</Grid>
</DataTemplate>
</FlipView.ItemTemplate>
</FlipView>
</Grid>
</Grid>
</Page>
and in the code behind file:
using DailyStyleApp;
using PortableAPI;
using System;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
namespace DailyStyleW8
{
/// <summary>
/// Display a list of recent updates to the user
/// </summary>
public sealed partial class MainPage : Page
{
Controller controller = new Controller();
public MainPage()
{
this.InitializeComponent();
}
/// <summary>
/// Invoked when this page is about to be displayed in a Frame.
/// </summary>
/// <param name="e">Event data that describes how this page was reached. The Parameter
/// property is typically used to configure the page.</param>
protected override void OnNavigatedTo(NavigationEventArgs e)
{
LoadContent();
}
private async void LoadContent()
{
var viewModel = await controller.GetMultiDayAsync(DateTime.Now, PortableAPIProvider.Storage.ReadFromSettings<int>("CacheDuration", 7));
displayViewer.ItemsSource = viewModel.Items;
displayViewer.Visibility = Windows.UI.Xaml.Visibility.Visible;
loadingViewer.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
}
}
}
Now when I run the code the LoadContent function is called correctly and the viewModel object is formed correctly. If I comment out the line displayViewer.ItemsSource = viewModel.Items; the ProgressBar visibility is changed as you would expect.
When that line is left in and stepped through all 4 lines inside the LoadContent method are run, however the FlipView is not updated with the new items and the ProgressBar visibility is not changed. viewModel.Items is of type List<T>.
I am even sure really what to be searching for here. I am guessing it's something wrong with the XAML and my binding?
The issue related to this question was actually to do with another section of code in the application. Elsewhere I had a series of async / await calls that were locking up the UI thread.
This prevented the scheduler from ever triggering the callbacks for the async. In short solution to the problem: Never call await on something that is called from the UI thread (and not via another async call).

Categories