Restricting Input in WPF - c#

I am creating a custom control that when invoked in the XAML can be set to only allow certain types of inputs:
<lib:CustomControl RestrictTo="UnsignedIntegersOnly" ... ></CustomControl>
Where the UnsignedIntegersOnly is part of an Enum containing the set of allowed restrictions.
If the user inputs something that is not allowed, the control will throw a validation error and not allow him to continue to the next form/page/etc.
My vision for implementing this, was to, in the underlying TextBox that makes up this control, bind its text field to a validation rule which will be passed as an input the RestrictTo value that was specified in the CustomControl XAML declaration. Then in that ValidationRule class, handle the RestrictTo specific validation and return whether the validation was successful or not.
This is where I am not quite sure how to proceed. Is it even possible to pass arguments to the ValidationRule in such a seemingly dynamic manner? I am setting a property, RestrictTo, of my control and then passing that to its validation.
If it is possible, how would it be done? What sort of binding or resource linking should I use?

You might be interested in using a MaskedTextBox control, it will restrict what the user can input in the TextBox.
As there's no official control from Microsoft for WPF I would suggest the following from Xceed :
MaskedTextBox (it's free to use :-)
Here you have the syntax of the mask :
http://msdn.microsoft.com/en-us/library/system.windows.forms.maskedtextbox.mask.aspx
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
Title="MainWindow" Height="350" Width="525">
<Grid>
<xctk:MaskedTextBox Mask="0000"></xctk:MaskedTextBox>
</Grid>
</Window>
If you have Visual Studio 2012 you can easily get this package through NuGet :
(right-click on your project )
Note : on my answer to your previous question, I extensively used validation rules in the link I posted but I'd say that if there are some times where you can avoid it through the means of a well-crafted component/control, then it's wise to do so. As #Barn pointed out on your previous question, a generic validation rule might be a hard thing to do and somewhat questionable as you'll have to handle all the types in it, IMO it's a little counter-intuitive as validators and converters are generally specific against being generalist; you're likely to waste more time on it than it's worth and it will be probably less re-usable than you think it could be. (source : my experience)

Below code should get you started. It is a user control and not a custom control. I recomend you get your code working as a user control first and then convert it to a custom control. Bind Valid property to some property in your viewmodel that controls user workflow.
XAML:
<UserControl x:Class="WpfApplication.ValidatingControl"
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" >
<StackPanel Orientation="Horizontal">
<TextBox Name="_textBox" TextChanged="OnTextChanged" Background="LightGray" Width="200"/>
<TextBlock Name="_messageText" Foreground="Red" />
</StackPanel>
</UserControl>
Code behind:
using System.Windows;
using System.Windows.Controls;
namespace WpfApplication
{
public partial class ValidatingControl : UserControl
{
public ValidatingControl()
{
InitializeComponent();
}
public enum Restrictions
{
UnsignedIntegersOnly,
SmallIntegersOnly
}
public static readonly DependencyProperty RestrictToProperty =
DependencyProperty.Register("RestrictTo", typeof(Restrictions), typeof(ValidatingControl), new PropertyMetadata(Restrictions.UnsignedIntegersOnly));
public Restrictions RestrictTo
{
get { return (Restrictions)GetValue(RestrictToProperty); }
set { SetValue(RestrictToProperty, value); }
}
public bool Valid
{
get { return (bool)GetValue(ValidProperty); }
set { SetValue(ValidProperty, value); }
}
public static readonly DependencyProperty ValidProperty =
DependencyProperty.Register("Valid", typeof(bool), typeof(ValidatingControl), new UIPropertyMetadata(false));
private void OnTextChanged(object sender, TextChangedEventArgs e)
{
ValidateText(RestrictTo, _textBox.Text);
}
private void ValidateText(Restrictions restrictTo, string text)
{
// validate text, update _messageText, update Valid
}
}
}

Related

C#/AvaloniaUI - Click a button and change Text

I am very new to AvaloniaUI.
I am really struggling to change a text when I click a button.
Here is my code:
<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"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="ReadyForWar_Launcher.MainWindow"
Title="ReadyForWar_Launcher">
<StackPanel>
<TextBlock Name="TestBlock">Show my text here!</TextBlock>
<Button Command="{Binding RunTheThing}" CommandParameter="Hello World">Change the Text!</Button>
</StackPanel>
</Window>
Here is my MainWindow.xaml.cs:
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace ReadyForWar_Launcher
{
public class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
#if DEBUG
this.AttachDevTools();
#endif
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
public void RunTheThing()
{
}
}
}
Inside RunTheThing I don't know how can I select the TextBlock with Name="TestBlock" and change the text to "Hello World".
Can you please help me out on this ?
There are two approaches, the recommended one and straightforward one.
Recommended: Use MVVM pattern. Create a view model with ButtonTextProperty and RunTheThing command, make the command to change the property, assign that model to the DataContext and bind your button text and command to view model properties. The MVVM approach is basically the same as in WPF, so you can use documentation and tutorials from there (that applies to most of the Avalonia, BTW). For example, here is a good one (not advertising, 4th link from google).
Straightforward (aka winforms-way): add x:Name="MyButton" to your button and use this.FindControl<Button>("MyButton") after calling AvaloniaXamlLoader.Load(this);. This will give you a Button reference that you can manipulate from code. Instead of using commands, you can just subscribe to the click handler directly from codebehind, add public void MyButton_OnClick(object sender, RoutedEventArgs args){} to your MainWindow class and add replace Command and CommandParameter with Click="MyButton_OnClick". That way button click will trigger your event handler.
Note, that the second approach doesn't scale well with the application size and suffers from code complexity when handling lists.

Binding to design data in WPF

I have a WPF window containing a ListBox. The ItemsSource is bound to a property of a view model.
<Window x:Class="SimpleWpfApp.View.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525"
DataContext="{Binding MainWindowViewModel, Source={StaticResource Locator}}">
<DockPanel>
<ListBox ItemsSource="{Binding SomeThings}" />
</DockPanel>
</Window>
The property of the view model is an observable collection of a custom interface; ISomeInterface. The interface is very simple and is implemented by SomeClass which additionally overrides ToString.
public class MainWindowViewModel
{
public ObservableCollection<ISomeInterface> SomeThings
{
get
{
var list = new List<ISomeInterface>
{
new SomeClass {Value = "initialised"},
new SomeClass {Value = "in"},
new SomeClass {Value = "code"}
};
return new ObservableCollection<ISomeInterface>(list);
}
}
}
public interface ISomeInterface
{
string Value { get; }
}
public class SomeClass : ISomeInterface
{
public string Value { get; set; }
public override string ToString() => Value;
}
When I view the window in Visual Studio 2015 or Blend all is as expected. ToString is called and the ListBox populated.
Blend screenshot
I have created XAML design data which I want to use when in design mode. I have added the design data in a directory called SampleData. I add a design datacontext statement to the window XAML immediately below the first DataContext.
d:DataContext="{d:DesignData Source=/SampleData/Data.xaml}"
This doesn't work. Visual Studio and Blend report 'File or project item not found' regardless of what I use for source path. I have tried /SampleData/Data.xaml, SampleData/Data.xaml, ../SampleData/Data.xaml, ./../SampleData/Data.xaml
Visual Studio and Blend only find Data.xaml if I move it out of the SampleData directory and into the project root. Then I am able to reference it using source path /Data.xaml or Data.xaml. If I use Data.xaml without prefixing / then Visual Studio and Blend report that the file cannot be found.. but find it anyway.
My first question is .. Can I use sample data in a sub-directory? And if so how?
Having successfully referenced Data.xaml in the project root, my window is not calling the overridden ToString so I'm getting a list of class name displayed. The list has the same number of items as the design data so it appears it is using the design data.
My second question is .. Why is the overridden ToString not being called here when it is if the objects are instantiated from code?
I'm aware I can achieve the desired result by specifying an item template.
<ListBox ItemsSource="{Binding SomeThings}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Value}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Full source is available for the example application on github
https://github.com/DangerousDarlow/WpfDesignData
UPDATE
Thanks to jstreet for answering. I changed the file properties for data.xaml in the sub directory and am now able to use this as design data. I thought I'd tried this before but I must be mistaken.
I'm still not seeing ToString being called. I tried changing the view model property to List<object> and also List<ISomeInterface> but both resulted in called to object.ToString; deduced by the display of the class name. I'll probably stop looking at this point as I'm not going to be using ToString anyway, I'll bind to the properties I want to display. It would be good to explain the difference in behaviour though.
I'm using Visual Studio 2015 community edition.
Here's some working sample code. You may want to refer to This article - MSDN.
In particular, note how to set properties for your Data.xaml file (Dictionary1.xaml, in my case) in your VS project:
Also note how to create your root object, SomeThings (SomeClasses in my case):
For collections, the root object can be an ArrayList or a custom type that derives from a collection or generic collection...
XAML:
<Window x:Class="WpfApplication277.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication277"
d:DataContext="{d:DesignData Source=/SampleData/Dictionary1.xaml}"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ListView ItemsSource="{Binding}"></ListView>
</Grid>
Dictionary1.xaml:
Right-click SampleData folder in your VS project, and select Add\New Item\WPF\Resource Dictionary, replace its contents with your design data. This should make sure your design data can be located in a sub-folder.
<m:SomeClasses xmlns:m="clr-namespace:WpfApplication277">
<m:SomeClass Value="design data 1">
</m:SomeClass>
<m:SomeClass Value="design data 2">
</m:SomeClass>
<m:SomeClass Value="design data 3">
</m:SomeClass>
SomeClasses: List<SomeClass> did NOT work !
public class SomeClasses : List<Object>
{
public SomeClasses() { }
}
SomeClass:
public class SomeClass : ISomeInterface
{
public string Value { get; set; }
public override string ToString() => string.Format("ToString() : {0}",Value);
}
Note that ToString() is definitely being called:

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.

Disappearing UserControl after build

The Issue: I have a newly made UserControl with a couple telerik controls in a parent Grid to be able to use it throughout the solution. Sounds simple enough right? I created the UserControl, let's call the Class My.Project.Controls.Tool, which I then tried to call to another View with the namespace xmlns:Controls="clr-namespace:My.Project.Controls;assembly=My.Project.GlobalUserControlDump" and then set it in the view via easily selected from the handy dandy intellisense.
This does as is expected, my UserControl appears on the separate view in the designer just fine. So I take the next normal step and build it....as soon as the build completes (which it does just fine with no errors reported as expected) the little bugger disappears! The xaml is still on the View of course, but it's disappeared from the designer AND it doesnt appear on the built solution?
Confused I go back, make a quick change to the UserControl and it appears back in the designer. Ok, I think it must be some fluke so I build it again....and again it disappears from the designer AND the built solution?
Now I can continue to reliably reproduce this scenario. Make a little change to the UserControl, it re-appears in the designer.....then build it and it disappears again!
Could someone pretty please shed some light on this quandry? Running in SL (both in and out of browser but built in browser) with Caliburn Micro. Any insight to this mystery is of course greatly appreciated, hoping another pair of eyes can catch my folly. Cheers!
For Clarification, this is what sits in the user control that directly related to a previous question.
<UserControl
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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
mc:Ignorable="d"
x:Class="My.Project.Controls.DatePicker">
<Grid Width="90">
<telerik:RadDateTimePicker
InputMode="DatePicker"
SelectedDate="{Binding SelectedDate, Mode=TwoWay}"/>
<telerik:RadMaskedDateTimeInput
IsClearButtonVisible="False"
FormatString="{}{0:M/d/yy}"
SelectionOnFocus="SelectAll"
Value="{Binding SelectedDate, Mode=TwoWay}"/>
</Grid>
</UserControl>
Which I would then invoke directly on a view like (It would sit in the GlobalUserControlDump project as the namespace shows) and once the namespace is added to the View, it shows up fine in the intellisense as expected;
<UserControl
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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:cal="clr-namespace:Caliburn.Micro;assembly=Caliburn.Micro"
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
x:Class="My.Project.Views.RandomView"
xmlns:Controls="clr-namespace:My.Project.Controls;assembly=My.Project.GlobalUserControlDump"
mc:Ignorable="d">
<Grid>
<Controls:DatePicker />
</Grid>
</UserControl>
Then I'm exposing the property I need via;
namespace My
{
public partial class DatePicker : UserControl
{
public static readonly DependencyProperty SelectedDateProperty =
DependencyProperty.Register("SelectedDate", typeof(DateTime), typeof(DatePicker), new PropertyMetadata(null));
public DatePicker()
{
// Required to initialize variables
DataContext = this;
}
public DateTime SelectedDate
{
get { return (DateTime)GetValue(SelectedDateProperty); }
set { SetValue(SelectedDateProperty, value); }
}
}
}
Thanks for taking a look, has me still currently stumped.
You are missing a call to InitializeComponent() in you UserControl's constructor.
I believe your namespace in the code you posted is incorrect.
By having new PropertyMetadata(null)); a property changed callback isn't being registered. Without that I believe binding won't work. What you want to do is bind to the property on your usercontrol and when the bound value changes, you want to set the value on the RadDateTimePicker contained in your control.
xaml:
<Grid Width="90">
<telerik:RadDateTimePicker x:Name="MyRadDateTimePicker" InputMode="DatePicker" />
<telerik:RadMaskedDateTimeInput x:Name="MyRadMaskedDateTimeInput"
IsClearButtonVisible="False"
FormatString="{}{0:M/d/yy}"
SelectionOnFocus="SelectAll" />
</Grid>
</UserControl>
code behind:
using System;
using System.Windows;
namespace SO
{
public partial class MyDatePicker
{
public MyDatePicker()
{
InitializeComponent();
}
public const string SelectedDatePropertyName = "SelectedDate";
public DateTime SelectedDate
{
get { return (DateTime)GetValue(SelectedDateProperty); }
set { SetValue(SelectedDateProperty, value); }
}
public static readonly DependencyProperty SelectedDateProperty = DependencyProperty.Register(
SelectedDatePropertyName,
typeof(DateTime),
typeof(MyDatePicker),
new PropertyMetadata(DateTime.Now, OnSelectedDatePropertyChanged));
private static void OnSelectedDatePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((MyDatePicker)d).MyRadDateTimePicker.SelectedDate = (DateTime) e.NewValue;
((MyDatePicker)d).MyRadMaskedDateTimeInput.Value = (DateTime)e.NewValue;
}
}
}

How to get C# attribute in the associated XAML?

I have a C# class like this :
public partial class MyClass: UserControl
{
public String SomeText{get;set;}
...
public MyClass(String Text)
{
this.InitializeComponent();
SomeText = Text;
}
}
And I would like to get the attribute SomeText in my XAML file. How to do something like this ?
<TextBlock Text="{THIS IS HERE I WANT TO HAVE SomeText}"></TextBlock>
I am new to C# so I don't know how to do. There must be a simple way?
Give the UserControl element a Name and then bind to it as in the example below. If your text isn't going to change you should define it before your call to InitialiseComponent.
<UserControl x:Class="WpfApplication1.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"
d:DesignHeight="300" d:DesignWidth="300"
x:Name="root">
<Grid>
<TextBlock Text="{Binding SomeText,ElementName=root}"/>
</Grid>
</UserControl>
If your SomeText is likely to change then you need to declare it as a DependencyProperty instead of a plain old string property. This would look something like below:
public string SomeText
{
get { return (string)GetValue(SomeTextProperty); }
set { SetValue(SomeTextProperty, value); }
}
// Using a DependencyProperty as the backing store for SomeText. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SomeTextProperty =
DependencyProperty.Register("SomeText", typeof(string), typeof(UserControl1), new UIPropertyMetadata(""));
You should then change your binding to look like the below, which will cause the UI to update when you change the value of SomeString.
<TextBlock Text="{Binding SomeText,ElementName=root,UpdateSourceTrigger=PropertyChanged}"/>
First of all, a correction:
that is a Property(MSDN), not an Attribute (MSDN), those are 2 completely different concepts.
Second:
WPF has a concept called DependencyProperties (MSDN) which you must leverage in order to build complex custom UI controls in WPF.
Third:
There are times when you do not really need to declare properties in your UI. WPF empowers you to separate UI from data via its powerful DataBinding Engine and the MVVM Pattern. So, think again whether or not this control you are declaring is the right place to declare your string property.
I suggest you get familiar with all these concepts if you're going to work in WPF.
Give me more details about what you're trying to do and I can give you more insight.
Edit:
Basically what you need to do is to declare a DependencyProperty in your control like this:
public static readonly DependencyProperty DisplayNameProperty = DependencyProperty.Register("DisplayName", typeof(string), typeof(mycontrol));
public string DisplayName
{
get { return GetValue(DisplayNameProperty).ToString(); }
set { SetValue(DisplayNameProperty, value); }
}
Then, in XAML:
<TextBlock Text="{Binding DisplayName, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}"/>
Please keep in mind that WPF requires a completely different mindset from other frameworks, so my suggestion still stands: get familiar with MVVM and Databinding if you're planning to seriously work in WPF.

Categories