TestStack White doesn't find TextBox in WPF Application - c#

I use TestStack White framework to automate testing of a WPF Application
It needs to open modal window and access TextBox in it. Everything works well, but White can't find Textbox, although it finds other elements of window
I tried the following lines of code:
TestStack.White.UIItems.TextBox TextBox = CreateBranch.Get<TestStack.White.UIItems.TextBox>(SearchCriteria.byAutomationId("Title"));
where CreateBranch is modal window
I also tried (SearchCriteria.All), (SearchCriteria.ByControlType) and nothing works
Coded UI tool finds this element well by AutomationID, but I need to do it in White
UISpy and other similar tools recognize this control and see its AutomationID
This textbox is custom control, here's code for it, I changed namespace name for privacy:
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Interactivity;
using System.Windows.Media;
namespace Test.Wpf.Controls.XTextBox
{
[TemplatePart(Name = "PART_Watermark", Type = typeof(TextBlock))]
[TemplatePart(Name = "PART_Pasword", Type = typeof(TextBlock))]
public class XTextBox : TextBox
{
#region Static
static XTextBox()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(XTextBox), new FrameworkPropertyMetadata(typeof(XTextBox)));
}
#endregion //Static
#region Fields
private TextBlock PART_Watermark;
private TextBlock PART_Pasword;
#endregion //Fields
#region DependencyProperties
public static readonly DependencyProperty WatermarkProperty = DependencyProperty.Register(
"Watermark",
typeof(String),
typeof(XTextBox),
new PropertyMetadata(String.Empty));
public static readonly DependencyProperty WatermarkVerticalAlignmentProperty = DependencyProperty.Register(
"WatermarkVerticalAlignment",
typeof(VerticalAlignment),
typeof(XTextBox),
new PropertyMetadata(VerticalAlignment.Stretch));
public static readonly DependencyProperty WatermarkForegroundProperty = DependencyProperty.Register(
"WatermarkForeground",
typeof(Brush),
typeof(XTextBox),
new PropertyMetadata(new SolidColorBrush(Colors.Black)));
public static readonly DependencyProperty WatermarkFontSizeProperty = DependencyProperty.Register(
"WatermarkFontSize",
typeof(Double),
typeof(XTextBox),
new PropertyMetadata(12.0));
public static readonly DependencyProperty IsFloatingProperty = DependencyProperty.Register(
"IsFloating",
typeof(Boolean),
typeof(XTextBox),
new PropertyMetadata(false));
public static readonly DependencyProperty IsAccessNegativeProperty = DependencyProperty.Register(
"IsAccessNegative",
typeof(Boolean),
typeof(XTextBox),
new PropertyMetadata(true));
public static readonly DependencyProperty IsDigitOnlyProperty = DependencyProperty.Register(
"IsDigitOnly",
typeof(Boolean),
typeof(XTextBox),
new PropertyMetadata(false));
public static readonly DependencyProperty MaxValueProperty = DependencyProperty.Register(
"MaxValue",
typeof(Single),
typeof(XTextBox),
new PropertyMetadata(Single.NaN));
public static readonly DependencyProperty IsPasswordProperty = DependencyProperty.Register(
"IsPassword",
typeof(Boolean),
typeof(XTextBox),
new PropertyMetadata(false));
public static readonly DependencyProperty VisibilityMainTextProperty = DependencyProperty.Register(
"VisibilityMainText",
typeof(Visibility),
typeof(XTextBox),
new PropertyMetadata(Visibility.Visible));
#endregion //DependencyProperties
#region Properties
[Description("Gets or sets the watermark title")]
public String Watermark
{
get { return (String)GetValue(WatermarkProperty); }
set { SetValue(WatermarkProperty, value); }
}
[Description("Gets or sets the watermark vertical alignment")]
public VerticalAlignment WatermarkVerticalAlignment
{
get { return (VerticalAlignment)GetValue(WatermarkVerticalAlignmentProperty); }
set { SetValue(WatermarkVerticalAlignmentProperty, value); }
}
[Description("Gets or sets the watermark title color")]
public Brush WatermarkForeground
{
get { return (Brush)GetValue(WatermarkVerticalAlignmentProperty); }
set { SetValue(WatermarkVerticalAlignmentProperty, value); }
}
[Description("Gets or sets the watermark title font size")]
public Double WatermarkFontSize
{
get { return (Double)GetValue(WatermarkVerticalAlignmentProperty); }
set { SetValue(WatermarkVerticalAlignmentProperty, value); }
}
[Description("Gets or sets the textbox floating mode")]
public Boolean IsFloating
{
get { return (Boolean)GetValue(IsFloatingProperty); }
set { SetValue(IsFloatingProperty, value); }
}
[Description("Gets or sets the textbox access of negative values")]
public Boolean IsAccessNegative
{
get { return (Boolean)GetValue(IsAccessNegativeProperty); }
set { SetValue(IsAccessNegativeProperty, value); }
}
[Description("Gets or sets the textbox chars type")]
public Boolean IsDigitOnly
{
get { return (Boolean)GetValue(IsDigitOnlyProperty); }
set { SetValue(IsDigitOnlyProperty, value); }
}
[Description("Gets or sets the max input value (enable in digit mode only)")]
public Single MaxValue
{
get { return (Single)GetValue(MaxValueProperty); }
set { SetValue(MaxValueProperty, value); }
}
[Description("Gets or sets the textbox is passwordbox")]
public Boolean IsPassword
{
get { return (Boolean)GetValue(IsPasswordProperty); }
set { SetValue(IsPasswordProperty, value); }
}
public Visibility VisibilityMainText
{
get { return (Visibility)GetValue(VisibilityMainTextProperty); }
set { SetValue(VisibilityMainTextProperty, value); }
}
#endregion //Properties
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
PART_Watermark = GetTemplateChild("PART_Watermark") as TextBlock;
PART_Pasword = GetTemplateChild("PART_Pasword") as TextBlock;
SetWatermarkVisibility();
if (IsPassword)
{
VisibilityMainText = Visibility.Collapsed;
if (PART_Pasword != null)
{
PART_Pasword.Visibility = Visibility.Visible;
PART_Pasword.FontSize = 20;
}
}
else
{
VisibilityMainText = Visibility.Visible;
}
DataObject.AddPastingHandler(this, OnPaste);
}
protected void OnPaste(Object sender, DataObjectPastingEventArgs e)
{
try
{
var isText = e.SourceDataObject.GetDataPresent(DataFormats.UnicodeText, true);
if (!isText) return;
var text = e.SourceDataObject.GetData(DataFormats.UnicodeText) as String;
if (!String.IsNullOrEmpty(text))
{
if (IsDigitOnly)
{
if (!IsAccessNegative)
{
var ch = text[0];
if (ch == 45)
{
e.CancelCommand();
}
}
for (int i = 0; i < text.Length; i++)
{
if (i == 0)
{
if (IsAccessNegative && text[i] == 45)
{
continue;
}
}
if (!Char.IsDigit(text[0]))
e.CancelCommand();
}
}
}
}
catch (Exception)
{
// ignored
e.Handled = true;
}
}
protected override void OnTextChanged(TextChangedEventArgs e)
{
base.OnTextChanged(e);
SetWatermarkVisibility();
if (IsPassword)
{
PART_Pasword.Text = new String('•', Text.Length);
}
}
protected override void OnLostKeyboardFocus(KeyboardFocusChangedEventArgs e)
{
base.OnLostKeyboardFocus(e);
SetWatermarkVisibility();
}
protected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e)
{
base.OnGotKeyboardFocus(e);
if (PART_Watermark != null)
{
PART_Watermark.Visibility = Visibility.Hidden;
}
}
protected override void OnPreviewTextInput(TextCompositionEventArgs e)
{
base.OnPreviewTextInput(e);
if (e.Text.Length == 0)
{
e.Handled = true;
return;
}
if (IsDigitOnly)
{
var ch = e.Text[0];
if (!Char.IsDigit(ch) && ch != 8 && ch != 46)
{
if (!(IsAccessNegative && ch == 45))
e.Handled = true;
}
if (IsFloating)
{
if (ch == 46 && Text.IndexOf('.') != -1)
{
e.Handled = true;
return;
}
}
if (!IsAccessNegative)
{
if (ch == 45)
{
e.Handled = true;
}
}
}
}
#region Private
private void SetWatermarkVisibility()
{
if (PART_Watermark != null)
{
PART_Watermark.Visibility = (Text != String.Empty || IsKeyboardFocused)? Visibility.Hidden : Visibility.Visible;
}
}
#endregion
}
}
Screenshot from UISpy

Let me know if that works
TextBox = (TextBox)CreateBranch
.Get(SearchCriteria.ByAutomationId("Title").AndOfFramework(WindowsFramework.Wpf));
Edited after new source added
You have to create a specific AutomationPeer for your custom control and return it via the override of the method OnCreateAutomationPeer().
Your control is a subclass of the TextBox control so you can just return a new TextBoxAutomationPeer instance or create your custom AutomationPeer from it.
public class XTextBox : TextBox
{
...
protected override AutomationPeer OnCreateAutomationPeer()
{
return new XTextBoxAutomationPeer(this);
// or just use the TextBoxAutomationPeer
// return new TextBoxAutomationPeer(this);
}
...
}
The custom automation peer
public class XTextBoxAutomationPeer : TextBoxAutomationPeer
{
public XTextBoxAutomationPeer(XTextBox owner)
: base(owner)
{
}
protected override string GetClassNameCore()
{
return "XTextBox";
}
}

[SetUpFixture]
public class SETUP_THAT_WILL_GET_CALL_LATER
{
[OneTimeSetUp]
public void OneTimeSetUp()
{
var applicationDirectory = TestContext.CurrentContext.TestDirectory;
var applicationPath = Path.Combine(applicationDirectory, #"..\..\..\, "your debug folder path here", "your application.exe here");
Application = Application.Launch(applicationPath);
Thread.Sleep(2000);
Window = Application.GetWindow("Title of your application", InitializeOption.WithCache);
}
[OneTimeTearDown()]
public void OneTimeTearDown()
{
Window.Dispose();
Application.Dispose();
}
public static Application Application;
public static Window Window;
}
Then in your test
[Test]
public void yourtest()
{
var textBox = SETUP_THAT_WILL_GET_CALL_LATER.**Window.Get<TextBox>("Your textbox name here");**
}

Related

Event for synchronizing all controls using the same Behavior

in my WPF application I have several forms with a multitude of input fields for the user. As it turns out not every user needs all fields depending on its companie's process therefore I have the new requirement to allow the user to hide fields depending on its own needs.
I was planning to use a Behavior for this which could be attached to basically every WPF-Control. The behavior would add a ContextMenu to each of the controls allowing to show/hide all of the available Fields. My current test project which seems to work nice has four DependencyProperties to make everything work:
string VisibilityGroupName:
This acts somehow as an Id for each field but is not unique in order to group multiple fields togther (eG a Label for a field caption to its corresponding TextBox). This string is currently also used as the name that the user sees in the ContextMenu.
Dictionary VisibilityDictionary:
This dictionary keeps track of all the visibility states of the fields. In my application I'm going to serialize this to XML in order to make the users decisions persistant.
bool AllowCustomVisibility:
This is just a flag in order to switch the whole funtionality off.
bool NotificationDummy:
This is where it gets interesting. Currently I abuse this property in conjunction with an ValueChanged-event to notify all the Controls that a state was changed so they can check if they are affected. While this works as expected I know that this is just a bad workaround because I don't know how the notification would be done correctly.
Does anybody have an idea how it is done correctly? I've marked the corresponding places in the code with TODOs:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interactivity;
namespace CustomizableUserControlVisibility
{
public class WPFCustomVisibilityBehavior : Behavior<DependencyObject>
{
#region Fields
private Control _control = null;
private ContextMenu _contextMenu;
private bool _contextMenuIsBuilt = false;
#endregion
#region Properties
public bool NotificationDummy
{
get { return (bool)GetValue(NotificationDummyProperty); }
set { SetValue(NotificationDummyProperty, value); }
}
public static readonly DependencyProperty NotificationDummyProperty = DependencyProperty.Register("NotificationDummy", typeof(bool), typeof(WPFCustomVisibilityBehavior), new PropertyMetadata(false));
public bool AllowCustomVisibility
{
get { return (bool)GetValue(AllowCustomVisibilityProperty); }
set { SetValue(AllowCustomVisibilityProperty, value); }
}
public static readonly DependencyProperty AllowCustomVisibilityProperty = DependencyProperty.Register("AllowCustomVisibility", typeof(bool), typeof(WPFCustomVisibilityBehavior), new PropertyMetadata(false));
public string VisibilityGroupName
{
get { return (string)GetValue(VisibilityGroupNameProperty); }
set { SetValue(VisibilityGroupNameProperty, value); }
}
public static readonly DependencyProperty VisibilityGroupNameProperty = DependencyProperty.Register("VisibilityGroupName", typeof(string), typeof(WPFCustomVisibilityBehavior), new PropertyMetadata(string.Empty));
public Dictionary<string, bool> VisibilityDictionary
{
get { return (Dictionary<string, bool>)GetValue(VisibilityDictionaryProperty); }
set { SetValue(VisibilityDictionaryProperty, value); }
}
public static readonly DependencyProperty VisibilityDictionaryProperty = DependencyProperty.Register("VisibilityDictionary", typeof(Dictionary<string, bool>), typeof(WPFCustomVisibilityBehavior), new PropertyMetadata(new Dictionary<string, bool>()));
#endregion
#region Constructor
public WPFCustomVisibilityBehavior()
{
// TODO: There should be a better way to notify other controls about state changes than this...
var temp = DependencyPropertyDescriptor.FromProperty(WPFCustomVisibilityBehavior.NotificationDummyProperty, typeof(WPFCustomVisibilityBehavior));
if (temp != null)
{
temp.AddValueChanged(this, OnNotificationDummyChanged);
}
}
#endregion
#region Overrrides
protected override void OnAttached()
{
base.OnAttached();
if (this.AllowCustomVisibility == false)
{
return;
}
this._control = this.AssociatedObject as Control;
if (!string.IsNullOrEmpty(this.VisibilityGroupName) && this._control != null)
{
if (this.VisibilityDictionary.ContainsKey(this.VisibilityGroupName))
{
if (this.VisibilityDictionary[this.VisibilityGroupName])
{
this._control.Visibility = Visibility.Visible;
}
else
{
this._control.Visibility = Visibility.Collapsed;
}
}
else
{
this.VisibilityDictionary.Add(this.VisibilityGroupName, this._control.Visibility == Visibility.Visible ? true : false);
}
// Add a ContextMenu to the Control, but only if it does not already have one (TextBox brings its default ContextMenu for copy, cut and paste)
if (this._control.ContextMenu == null && !(this._control is TextBox))
{
this._contextMenu = new ContextMenu();
ContextMenuService.SetContextMenu(this._control, this._contextMenu);
this._control.ContextMenuOpening += (sender, e) => { ContextMenuOpening(e); };
}
}
}
#endregion
#region Event handling
private void ContextMenuOpening(ContextMenuEventArgs e)
{
if (this._contextMenuIsBuilt == false)
{
this._contextMenu.Items.Clear();
Dictionary<string, MenuItem> menuItems = new Dictionary<string, MenuItem>();
foreach (string k in this.VisibilityDictionary.Keys)
{
MenuItem menuItem = new MenuItem() { Header = k, Name = k, IsCheckable = true, StaysOpenOnClick = true };
menuItem.Click += MenuItem_Click;
menuItems.Add(k, menuItem);
}
var keyList = menuItems.Keys.ToList();
keyList.Sort();
foreach (string key in keyList)
{
this._contextMenu.Items.Add(menuItems[key]);
}
this._contextMenuIsBuilt = true;
}
foreach (MenuItem mi in this._contextMenu.Items)
{
mi.IsChecked = this.VisibilityDictionary[mi.Name];
}
}
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
MenuItem mi = sender as MenuItem;
if (mi != null && this.VisibilityDictionary != null && this.VisibilityDictionary.ContainsKey(mi.Name))
{
this.VisibilityDictionary[mi.Name] = mi.IsChecked;
// TODO: There should be a better way to notify other controls about state changes than this...
this.NotificationDummy = !NotificationDummy;
}
}
private void OnNotificationDummyChanged(object sender, EventArgs args)
{
// TODO: There should be a better way to notify other controls about state changes than this...
if (this._control != null && this.VisibilityDictionary != null && !string.IsNullOrEmpty(this.VisibilityGroupName))
{
if (this.VisibilityDictionary.ContainsKey(this.VisibilityGroupName))
{
if (this.VisibilityDictionary[this.VisibilityGroupName])
{
this._control.Visibility = Visibility.Visible;
}
else
{
this._control.Visibility = Visibility.Collapsed;
}
}
}
}
#endregion
}
}
Due to the lack of any other ideas I decided to use a static Event which seems to solve my problem quite well and this approach at leasts saves me the NotificationDummy-DependencyProperty which I had to use in the first place.
If anybody is interested - here is my final solution:
namespace CustomizableUserControlVisibility
{
public delegate void VisibilityChangedEventHandler(object visibilityDictionary);
public class WPFCustomVisibilityBehavior : Behavior<DependencyObject>
{
#region Fields
public static event VisibilityChangedEventHandler OnVisibilityChanged;
private Control _control = null;
private ContextMenu _contextMenu;
private bool _contextMenuIsBuilt = false;
#endregion
#region Properties
public bool AllowCustomVisibility
{
get { return (bool)GetValue(AllowCustomVisibilityProperty); }
set { SetValue(AllowCustomVisibilityProperty, value); }
}
public static readonly DependencyProperty AllowCustomVisibilityProperty = DependencyProperty.Register("AllowCustomVisibility", typeof(bool), typeof(WPFCustomVisibilityBehavior), new PropertyMetadata(false));
public string VisibilityGroupName
{
get { return (string)GetValue(VisibilityGroupNameProperty); }
set { SetValue(VisibilityGroupNameProperty, value); }
}
public static readonly DependencyProperty VisibilityGroupNameProperty = DependencyProperty.Register("VisibilityGroupName", typeof(string), typeof(WPFCustomVisibilityBehavior), new PropertyMetadata(string.Empty));
public Dictionary<string, bool> VisibilityDictionary
{
get { return (Dictionary<string, bool>)GetValue(VisibilityDictionaryProperty); }
set { SetValue(VisibilityDictionaryProperty, value); }
}
public static readonly DependencyProperty VisibilityDictionaryProperty = DependencyProperty.Register("VisibilityDictionary", typeof(Dictionary<string, bool>), typeof(WPFCustomVisibilityBehavior), new PropertyMetadata(new Dictionary<string, bool>()));
#endregion
#region Constructor
public WPFCustomVisibilityBehavior()
{
OnVisibilityChanged += VisibilityChanged;
}
#endregion
#region Overrrides
protected override void OnAttached()
{
base.OnAttached();
if (this.AllowCustomVisibility == false)
{
return;
}
this._control = this.AssociatedObject as Control;
if (!string.IsNullOrEmpty(this.VisibilityGroupName) && this._control != null)
{
if (this.VisibilityDictionary.ContainsKey(this.VisibilityGroupName))
{
if (this.VisibilityDictionary[this.VisibilityGroupName])
{
this._control.Visibility = Visibility.Visible;
}
else
{
this._control.Visibility = Visibility.Collapsed;
}
}
else
{
this.VisibilityDictionary.Add(this.VisibilityGroupName, this._control.Visibility == Visibility.Visible ? true : false);
}
}
// Add a ContextMenu to the Control, but only if it does not already have one (TextBox brings its default ContextMenu for copy, cut and paste)
if (this._control != null && this._control.ContextMenu == null && !(this._control is TextBox))
{
this._contextMenu = new ContextMenu();
ContextMenuService.SetContextMenu(this._control, this._contextMenu);
this._control.ContextMenuOpening += (sender, e) => { ContextMenuOpening(e); };
}
}
#endregion
#region Event handling
private void ContextMenuOpening(ContextMenuEventArgs e)
{
if (this._contextMenuIsBuilt == false)
{
// Clear Items just to be sure there is nothing in it...
this._contextMenu.Items.Clear();
// Create default items first
MenuItem showAll = new MenuItem() { Header = "Show all optional fields", IsCheckable = false, FontWeight = FontWeights.Bold };
showAll.Click += MenuItem_ShowAll_Click;
MenuItem hideAll = new MenuItem() { Header = "Hide all optional fields", IsCheckable = false, FontWeight = FontWeights.Bold };
hideAll.Click += MenuItem_HideAll_Click;
// Create field items and sort them by name
Dictionary<string, MenuItem> menuItems = new Dictionary<string, MenuItem>();
foreach (string k in this.VisibilityDictionary.Keys)
{
MenuItem menuItem = new MenuItem() { Header = k, Name = k, IsCheckable = true, StaysOpenOnClick = true };
menuItem.Click += MenuItem_Click;
menuItems.Add(k, menuItem);
}
var keyList = menuItems.Keys.ToList();
keyList.Sort();
// Now add default items followed by field items
this._contextMenu.Items.Add(showAll);
this._contextMenu.Items.Add(hideAll);
this._contextMenu.Items.Add(new Separator());
foreach (string key in keyList)
{
this._contextMenu.Items.Add(menuItems[key]);
}
this._contextMenuIsBuilt = true;
}
foreach (Object o in this._contextMenu.Items)
{
MenuItem mi = o as MenuItem;
if (mi != null && mi.FontWeight != FontWeights.Bold)
{
mi.IsChecked = this.VisibilityDictionary[mi.Name];
}
}
}
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
MenuItem mi = sender as MenuItem;
if (mi != null && this.VisibilityDictionary != null && this.VisibilityDictionary.ContainsKey(mi.Name))
{
this.VisibilityDictionary[mi.Name] = mi.IsChecked;
OnVisibilityChanged(this.VisibilityDictionary);
}
}
private void MenuItem_HideAll_Click(object sender, RoutedEventArgs e)
{
List<string> keys = this.VisibilityDictionary.Keys.ToList<string>();
foreach (string key in keys)
{
this.VisibilityDictionary[key] = false;
}
OnVisibilityChanged(this.VisibilityDictionary);
}
private void MenuItem_ShowAll_Click(object sender, RoutedEventArgs e)
{
List<string> keys = this.VisibilityDictionary.Keys.ToList<string>();
foreach (string key in keys)
{
this.VisibilityDictionary[key] = true;
}
OnVisibilityChanged(this.VisibilityDictionary);
}
private void VisibilityChanged(object visibilityDictionary)
{
if (this._control != null && this.VisibilityDictionary != null && this.VisibilityDictionary == visibilityDictionary && !string.IsNullOrEmpty(this.VisibilityGroupName))
{
if (this.VisibilityDictionary.ContainsKey(this.VisibilityGroupName))
{
if (this.VisibilityDictionary[this.VisibilityGroupName])
{
this._control.Visibility = Visibility.Visible;
}
else
{
this._control.Visibility = Visibility.Collapsed;
}
}
}
}
#endregion
}
}

WinRt: Binding a RTF String to a RichEditBox

Searched a long time to bind some RTF text to an RichEditBox Control on Windows Store Applications. Even it should function in TwoMay Binding Mode.
...
... finally I found the following solution. I created a inherited control from the original RichEditBox control with a DependencyProperty RtfText.
public class RichEditBoxExtended : RichEditBox
{
public static readonly DependencyProperty RtfTextProperty =
DependencyProperty.Register(
"RtfText", typeof (string), typeof (RichEditBoxExtended),
new PropertyMetadata(default(string), RtfTextPropertyChanged));
private bool _lockChangeExecution;
public RichEditBoxExtended()
{
TextChanged += RichEditBoxExtended_TextChanged;
}
public string RtfText
{
get { return (string) GetValue(RtfTextProperty); }
set { SetValue(RtfTextProperty, value); }
}
private void RichEditBoxExtended_TextChanged(object sender, RoutedEventArgs e)
{
if (!_lockChangeExecution)
{
_lockChangeExecution = true;
string text;
Document.GetText(TextGetOptions.None, out text);
if (string.IsNullOrWhiteSpace(text))
{
RtfText = "";
}
else
{
Document.GetText(TextGetOptions.FormatRtf, out text);
RtfText = text;
}
_lockChangeExecution = false;
}
}
private static void RtfTextPropertyChanged(DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
var rtb = dependencyObject as RichEditBoxExtended;
if (rtb == null) return;
if (!rtb._lockChangeExecution)
{
rtb._lockChangeExecution = true;
rtb.Document.SetText(TextSetOptions.FormatRtf, rtb.RtfText);
rtb._lockChangeExecution = false;
}
}
}
This solution works for me - perhaps for others too. :-)
Known issues: strange behaviours in VirtualizingStackPanel.VirtualizationMode="Recycling"

How to define an extend stackpanel control with two child dependency properties

I have to write an ExtendedStackPanel control which will have two dependency properties like this.
<ExtendedStackPanel IsReadOnly="{Binding Item.IsReadOnly, Mode=OneWay}" >
<TemplateTrue>
... control visible when isreadonly is true
</TemplateTrue>
<TemplateFalse>
... control visible when isreadonly is false
</TemplateFalse>
</ExtendedStackPanel>
I've written this to achieve the goal but it's not working.
public class ExtendedStackPanel : StackPanel
{
public ExtendedStackPanel()
: base()
{
this.Orientation = System.Windows.Controls.Orientation.Vertical;
}
#region IsReadOnly
public bool IsReadOnly
{
get { return (bool)GetValue(IsReadOnlyProperty); }
set { SetValue(IsReadOnlyProperty, value); }
}
public static readonly DependencyProperty IsReadOnlyProperty =
DependencyProperty.Register("IsReadOnly", typeof(bool),
typeof(ExtendedStackPanel), new PropertyMetadata(new PropertyChangedCallback(OnReadOnlyChanged)));
static void OnReadOnlyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var visible = (bool)e.NewValue;
var control = d as ExtendedStackPanel;
if (visible)
{
control.TemplateTrue.Visibility = Visibility.Visible;
control.TemplateFalse.Visibility = Visibility.Collapsed;
}
else
{
control.TemplateTrue.Visibility = Visibility.Collapsed;
control.TemplateFalse.Visibility = Visibility.Visible;
}
}
#endregion
#region TemplateTrue
public Control TemplateTrue
{
get { return (Control)GetValue(TemplateTrueProperty); }
set { SetValue(TemplateTrueProperty, value); }
}
public static readonly DependencyProperty TemplateTrueProperty =
DependencyProperty.Register("TemplateTrue", typeof(Control),
typeof(ExtendedStackPanel), new PropertyMetadata(new PropertyChangedCallback(OnTemplateTrueChanged)));
static void OnTemplateTrueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as ExtendedStackPanel;
control.TemplateTrue.Visibility = control.IsReadOnly ? Visibility.Visible : Visibility.Collapsed;
}
#endregion
#region TemplateFalse
public Control TemplateFalse
{
get { return (Control)GetValue(TemplateFalseProperty); }
set { SetValue(TemplateFalseProperty, value); }
}
public static readonly DependencyProperty TemplateFalseProperty =
DependencyProperty.Register("TemplateFalse", typeof(Control),
typeof(ExtendedStackPanel), new PropertyMetadata(new PropertyChangedCallback(OnTemplateFalseChanged)));
static void OnTemplateFalseChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as ExtendedStackPanel;
control.TemplateFalse.Visibility = !control.IsReadOnly ? Visibility.Visible : Visibility.Collapsed;
}
#endregion
}
In my code, I have put a combobox control when IsReadOnly is set to false, and a simple textbox when IsReadOnly is set to true but nothing is displayed when code is run.
Help me please.
Finally, I choose to use a UserControl, in which I put two ContentControl.
In code behind of the UserControl, I write this:
public partial class ConditionalControl : UserControl, INotifyPropertyChanged
{
public ConditionalControl()
{
InitializeComponent();
this.IsReadOnly = false;
}
#region IsReadOnly
public bool IsReadOnly
{
get { return (bool)GetValue(IsReadOnlyProperty); }
set { SetValue(IsReadOnlyProperty, value); }
}
public static readonly DependencyProperty IsReadOnlyProperty =
DependencyProperty.Register("IsReadOnly", typeof(bool),
typeof(ConditionalControl), new PropertyMetadata(new PropertyChangedCallback(OnReadOnlyChanged)));
static void OnReadOnlyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var visible = (bool)e.NewValue;
var control = d as ConditionalControl;
if (visible)
{
control.TemplateTrueContent.Visibility = Visibility.Visible;
control.TemplateFalseContent.Visibility = Visibility.Collapsed;
}
else
{
control.TemplateTrueContent.Visibility = Visibility.Collapsed;
control.TemplateFalseContent.Visibility = Visibility.Visible;
}
}
#endregion
#region TemplateFalse
public object TemplateFalse
{
get { return (object)GetValue(TemplateFalseProperty); }
set { SetValue(TemplateFalseProperty, value); }
}
public static readonly DependencyProperty TemplateFalseProperty =
DependencyProperty.Register("TemplateFalse", typeof(object),
typeof(ConditionalControl), new PropertyMetadata(new PropertyChangedCallback(OnTemplateFalseChanged)));
static void OnTemplateFalseChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as ConditionalControl;
control.TemplateFalseContent.Visibility = !control.IsReadOnly ? Visibility.Visible : Visibility.Collapsed;
control.OnPropertyChanged("TemplateFalse");
}
#endregion
#region TemplateTrue
public object TemplateTrue
{
get { return (object)GetValue(TemplateTrueProperty); }
set { SetValue(TemplateTrueProperty, value); }
}
public static readonly DependencyProperty TemplateTrueProperty =
DependencyProperty.Register("TemplateTrue", typeof(object),
typeof(ConditionalControl), new PropertyMetadata(new PropertyChangedCallback(OnTemplateTrueChanged)));
static void OnTemplateTrueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as ConditionalControl;
control.TemplateTrueContent.Visibility = control.IsReadOnly ? Visibility.Visible : Visibility.Collapsed;
control.OnPropertyChanged("TemplateTrue");
}
#endregion
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (propertyName == "TemplateFalse")
{
this.TemplateFalseContent.Content = this.TemplateFalse;
}
if (propertyName == "TemplateTrue")
{
this.TemplateTrueContent.Content = this.TemplateTrue;
}
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Hope this will help someone.

Module not rendering

In my application i'm trying to load the main module trough code. Everything works up to the point of rendering and i have NO clue why it isn't rendering. The content has the right values and everything.. My guess is that something went wibbly wobbly and I seem to be missing it.
Control that holds the view
[ContentPropertyAttribute("ContentView")]
public class ContentControlExtened : ContentControl
{
public static readonly DependencyProperty ContentProperty =
DependencyProperty.Register(
"Type",
typeof(Type),
typeof(ContentControl),
new FrameworkPropertyMetadata(null, ContentTypeChanged));
public static readonly DependencyProperty ContentViewProperty =
DependencyProperty.Register(
"View",
typeof(FrameworkElement),
typeof(ContentControl),
new FrameworkPropertyMetadata(null, ContentViewChanged));
private static void ContentViewChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
//throw new NotImplementedException();
}
public ContentControlExtened()
{
this.Loaded += ContentControlLoaded;
}
private void ContentControlLoaded(object sender, RoutedEventArgs e)
{
this.LoadContent();
}
private void LoadContent()
{
UserControl u = null;
if (Type != null)
{
u = (UserControl)ServiceLocator.Current.GetInstance(this.Type);
}
u.Background = new SolidColorBrush(Color.FromRgb(0, 0, 255));
this.View = u;
}
public Type Type
{
get { return (Type)GetValue(ContentProperty); }
set { SetValue(ContentProperty, value); }
}
public FrameworkElement View
{
get { return (FrameworkElement)GetValue(ContentProperty); }
set
{
SetValue(ContentProperty, value);
}
}
}
Method in shell to load the main view of the given moduleInfo
private void OpenMainView(ModuleInfo module)
{
Type moduleType = Type.GetType(module.ModuleType);
var moduleMainViewType = moduleType.Assembly.GetType(moduleType.Namespace + ".Views.MainView");
this.ContentHolder.MainContent.Type = moduleMainViewType;
}
The contructor of the mainview is straight forward. Just standard control InitializeComponent() and Datacontext is being set trough the servicelocator.
public FrameworkElement View
{
get { return (FrameworkElement)GetValue(ContentProperty); }
set
{
SetValue(ContentProperty, value);
}
}
Here you are getting and setting the ContentProperty. It should be ContentViewProperty.

Applying MahApps.Metro style to NavigationWindow

Has anyone had any luck applying the MahApps.Metro style to a NavigationWindow? I have implemented it for a Window just fine, but need to apply it to a NavigationWindow with Pages. I tried extending NavigationWindow and adding the modifications from MetroWindow like so, but no luck. The Window has a standard title bar and border, and the content is completely black.
using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Navigation;
using MahApps.Metro.Native;
namespace MahApps.Metro.Controls
{
[TemplatePart(Name = PART_TitleBar, Type = typeof(UIElement))]
[TemplatePart(Name = PART_WindowCommands, Type = typeof(WindowCommands))]
public class MetroNavigationWindow : NavigationWindow
{
private const string PART_TitleBar = "PART_TitleBar";
private const string PART_WindowCommands = "PART_WindowCommands";
public static readonly DependencyProperty ShowIconOnTitleBarProperty = DependencyProperty.Register("ShowIconOnTitleBar", typeof(bool), typeof(MetroNavigationWindow), new PropertyMetadata(true));
public static readonly DependencyProperty ShowTitleBarProperty = DependencyProperty.Register("ShowTitleBar", typeof(bool), typeof(MetroNavigationWindow), new PropertyMetadata(true));
public static readonly DependencyProperty ShowMinButtonProperty = DependencyProperty.Register("ShowMinButton", typeof(bool), typeof(MetroNavigationWindow), new PropertyMetadata(true));
public static readonly DependencyProperty ShowCloseButtonProperty = DependencyProperty.Register("ShowCloseButton", typeof(bool), typeof(MetroNavigationWindow), new PropertyMetadata(true));
public static readonly DependencyProperty ShowMaxRestoreButtonProperty = DependencyProperty.Register("ShowMaxRestoreButton", typeof(bool), typeof(MetroNavigationWindow), new PropertyMetadata(true));
public static readonly DependencyProperty TitlebarHeightProperty = DependencyProperty.Register("TitlebarHeight", typeof(int), typeof(MetroNavigationWindow), new PropertyMetadata(30));
public static readonly DependencyProperty TitleCapsProperty = DependencyProperty.Register("TitleCaps", typeof(bool), typeof(MetroNavigationWindow), new PropertyMetadata(true));
public static readonly DependencyProperty SaveWindowPositionProperty = DependencyProperty.Register("SaveWindowPosition", typeof(bool), typeof(MetroNavigationWindow), new PropertyMetadata(false));
public static readonly DependencyProperty WindowPlacementSettingsProperty = DependencyProperty.Register("WindowPlacementSettings", typeof(IWindowPlacementSettings), typeof(MetroNavigationWindow), new PropertyMetadata(null));
public static readonly DependencyProperty TitleForegroundProperty = DependencyProperty.Register("TitleForeground", typeof(Brush), typeof(MetroNavigationWindow));
public static readonly DependencyProperty IgnoreTaskbarOnMaximizeProperty = DependencyProperty.Register("IgnoreTaskbarOnMaximize", typeof(bool), typeof(MetroNavigationWindow), new PropertyMetadata(false));
public static readonly DependencyProperty GlowBrushProperty = DependencyProperty.Register("GlowBrush", typeof(SolidColorBrush), typeof(MetroNavigationWindow), new PropertyMetadata(null));
public static readonly DependencyProperty FlyoutsProperty = DependencyProperty.Register("Flyouts", typeof(FlyoutsControl), typeof(MetroNavigationWindow), new PropertyMetadata(null));
public static readonly DependencyProperty WindowTransitionsEnabledProperty = DependencyProperty.Register("WindowTransitionsEnabled", typeof(bool), typeof(MetroNavigationWindow), new PropertyMetadata(true));
bool isDragging;
public bool WindowTransitionsEnabled
{
get { return (bool)this.GetValue(WindowTransitionsEnabledProperty); }
set { SetValue(WindowTransitionsEnabledProperty, value); }
}
public FlyoutsControl Flyouts
{
get { return (FlyoutsControl)GetValue(FlyoutsProperty); }
set { SetValue(FlyoutsProperty, value); }
}
public WindowCommands WindowCommands { get; set; }
public bool IgnoreTaskbarOnMaximize
{
get { return (bool)this.GetValue(IgnoreTaskbarOnMaximizeProperty); }
set { SetValue(IgnoreTaskbarOnMaximizeProperty, value); }
}
public Brush TitleForeground
{
get { return (Brush)GetValue(TitleForegroundProperty); }
set { SetValue(TitleForegroundProperty, value); }
}
public bool SaveWindowPosition
{
get { return (bool)GetValue(SaveWindowPositionProperty); }
set { SetValue(SaveWindowPositionProperty, value); }
}
public IWindowPlacementSettings WindowPlacementSettings
{
get { return (IWindowPlacementSettings)GetValue(WindowPlacementSettingsProperty); }
set { SetValue(WindowPlacementSettingsProperty, value); }
}
public bool ShowIconOnTitleBar
{
get { return (bool)GetValue(ShowIconOnTitleBarProperty); }
set { SetValue(ShowIconOnTitleBarProperty, value); }
}
public bool ShowTitleBar
{
get { return (bool)GetValue(ShowTitleBarProperty); }
set { SetValue(ShowTitleBarProperty, value); }
}
public bool ShowMinButton
{
get { return (bool)GetValue(ShowMinButtonProperty); }
set { SetValue(ShowMinButtonProperty, value); }
}
public bool ShowCloseButton
{
get { return (bool)GetValue(ShowCloseButtonProperty); }
set { SetValue(ShowCloseButtonProperty, value); }
}
public int TitlebarHeight
{
get { return (int)GetValue(TitlebarHeightProperty); }
set { SetValue(TitlebarHeightProperty, value); }
}
public bool ShowMaxRestoreButton
{
get { return (bool)GetValue(ShowMaxRestoreButtonProperty); }
set { SetValue(ShowMaxRestoreButtonProperty, value); }
}
public bool TitleCaps
{
get { return (bool)GetValue(TitleCapsProperty); }
set { SetValue(TitleCapsProperty, value); }
}
public SolidColorBrush GlowBrush
{
get { return (SolidColorBrush)GetValue(GlowBrushProperty); }
set { SetValue(GlowBrushProperty, value); }
}
public string WindowTitle
{
get { return TitleCaps ? Title.ToUpper() : Title; }
}
public MetroNavigationWindow()
{
Loaded += this.MetroWindow_Loaded;
}
private void MetroWindow_Loaded(object sender, RoutedEventArgs e)
{
VisualStateManager.GoToState(this, "AfterLoaded", true);
if (!ShowTitleBar)
{
//Disables the system menu for reasons other than clicking an invisible titlebar.
IntPtr handle = new WindowInteropHelper(this).Handle;
UnsafeNativeMethods.SetWindowLong(handle, UnsafeNativeMethods.GWL_STYLE, UnsafeNativeMethods.GetWindowLong(handle, UnsafeNativeMethods.GWL_STYLE) & ~UnsafeNativeMethods.WS_SYSMENU);
}
if (this.Flyouts == null)
{
this.Flyouts = new FlyoutsControl();
}
}
static MetroNavigationWindow()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MetroNavigationWindow), new FrameworkPropertyMetadata(typeof(MetroNavigationWindow)));
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
if (WindowCommands == null)
WindowCommands = new WindowCommands();
var titleBar = GetTemplateChild(PART_TitleBar) as UIElement;
if (ShowTitleBar && titleBar != null)
{
titleBar.MouseDown += TitleBarMouseDown;
titleBar.MouseUp += TitleBarMouseUp;
titleBar.MouseMove += TitleBarMouseMove;
}
else
{
MouseDown += TitleBarMouseDown;
MouseUp += TitleBarMouseUp;
MouseMove += TitleBarMouseMove;
}
}
protected override void OnStateChanged(EventArgs e)
{
if (WindowCommands != null)
{
WindowCommands.RefreshMaximiseIconState();
}
base.OnStateChanged(e);
}
protected void TitleBarMouseDown(object sender, MouseButtonEventArgs e)
{
var mousePosition = e.GetPosition(this);
bool isIconClick = ShowIconOnTitleBar && mousePosition.X <= TitlebarHeight && mousePosition.Y <= TitlebarHeight;
if (e.ChangedButton == MouseButton.Left)
{
if (isIconClick)
{
if (e.ClickCount == 2)
{
Close();
}
else
{
ShowSystemMenuPhysicalCoordinates(this, PointToScreen(new Point(0, TitlebarHeight)));
}
}
else
{
IntPtr windowHandle = new WindowInteropHelper(this).Handle;
UnsafeNativeMethods.ReleaseCapture();
var wpfPoint = PointToScreen(Mouse.GetPosition(this));
short x = Convert.ToInt16(wpfPoint.X);
short y = Convert.ToInt16(wpfPoint.Y);
int lParam = x | (y << 16);
UnsafeNativeMethods.SendMessage(windowHandle, Constants.WM_NCLBUTTONDOWN, Constants.HT_CAPTION, lParam);
if (e.ClickCount == 2 && (ResizeMode == ResizeMode.CanResizeWithGrip || ResizeMode == ResizeMode.CanResize))
{
WindowState = WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized;
}
}
}
else if (e.ChangedButton == MouseButton.Right)
{
ShowSystemMenuPhysicalCoordinates(this, PointToScreen(mousePosition));
}
}
protected void TitleBarMouseUp(object sender, MouseButtonEventArgs e)
{
isDragging = false;
}
private void TitleBarMouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton != MouseButtonState.Pressed)
{
isDragging = false;
}
if (isDragging
&& WindowState == WindowState.Maximized
&& ResizeMode != ResizeMode.NoResize)
{
// Calculating correct left coordinate for multi-screen system.
Point mouseAbsolute = PointToScreen(Mouse.GetPosition(this));
double width = RestoreBounds.Width;
double left = mouseAbsolute.X - width / 2;
// Check if the mouse is at the top of the screen if TitleBar is not visible
if (!ShowTitleBar && mouseAbsolute.Y > TitlebarHeight)
return;
// Aligning window's position to fit the screen.
double virtualScreenWidth = SystemParameters.VirtualScreenWidth;
left = left + width > virtualScreenWidth ? virtualScreenWidth - width : left;
var mousePosition = e.MouseDevice.GetPosition(this);
// When dragging the window down at the very top of the border,
// move the window a bit upwards to avoid showing the resize handle as soon as the mouse button is released
Top = mousePosition.Y < 5 ? -5 : mouseAbsolute.Y - mousePosition.Y;
Left = left;
// Restore window to normal state.
WindowState = WindowState.Normal;
}
}
internal T GetPart<T>(string name) where T : DependencyObject
{
return (T)GetTemplateChild(name);
}
private static void ShowSystemMenuPhysicalCoordinates(Window window, Point physicalScreenLocation)
{
if (window == null) return;
var hwnd = new WindowInteropHelper(window).Handle;
if (hwnd == IntPtr.Zero || !UnsafeNativeMethods.IsWindow(hwnd))
return;
var hmenu = UnsafeNativeMethods.GetSystemMenu(hwnd, false);
var cmd = UnsafeNativeMethods.TrackPopupMenuEx(hmenu, Constants.TPM_LEFTBUTTON | Constants.TPM_RETURNCMD, (int)physicalScreenLocation.X, (int)physicalScreenLocation.Y, hwnd, IntPtr.Zero);
if (0 != cmd)
UnsafeNativeMethods.PostMessage(hwnd, Constants.SYSCOMMAND, new IntPtr(cmd), IntPtr.Zero);
}
}
}
To accomplish this, I am using a MetroWindow as my main navigation window, and a Frame within that MetroWindow which handles the navigation.
Navigation Window
<Controls:MetroWindow x:Class="TestWpfApplicationMahApps.Metro.NavWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro" Title="NavWindow" Height="300" Width="300">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Colours.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/Blue.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/BaseLight.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Frame Source="Page1.xaml" NavigationUIVisibility="Hidden"></Frame>
</Controls:MetroWindow>
This allows me to not change any of the navigation logic, it can be called from the NavigationService just like it was when using a NavigationWindow.
Page 1 .cs
/// <summary>
/// Interaction logic for Page1.xaml
/// </summary>
public partial class Page1 : Page
{
public Page1()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
NavigationService.Navigate(new Page2());
}
}

Categories