Attached Property and DynamicResource - c#

I'm trying to do set a DynamicResource on a content of a Attached Property but this isn't working. Trying to understand why, but I can't figure it out.
Basically, I'm implementing a watermark on a textbox using the code available at: Watermark / hint text / placeholder TextBox in WPF
provided by John Myczek
and using it as so:
<TextBox Text="{Binding SomeProperty}">
<helper:WatermarkService.Watermark>
<TextBlock FontStyle="Italic" Text="{DynamicResource SomeResource}" />
</helper:WatermarkService.Watermark>
</TextBox>
the inner TextBlock works just fine if it is outside the WatermarkService.Watermark attached property. The SomeResource for some reason is empty.
My resources are being loaded as so this.Resources.MergedDictionaries.Add(lastDictionary); since the app is localized and the data is being retrieved from a central place.
Do the controls on the attached properties share the same resources set as their "parents"? What is wrong here?
Thank you

The problem is clear. Dynamic resources are resolved by parsing upwards the logical tree. The dynamic resource is not found because your textblock is not in the correct logical tree, probably he does not have a logical parent and that is why the resource is not found.
You could solve it by adding it to the correct logical tree like for example it could be the child of the textbox. It is not so trivial and depends also on the usage that is required, because the customization of the logical tree is not so trivial.
There is not so simple like having a public method AddLogicalChild because then you would mess up the entire system. Now the question is who has the responsibility of doing this. The general solution could be to have a custom TextBox that overrides logical children related methods and returns also the watermark textblock.
It is not the global solution but in your case you could have a custom textbox overriding the LogicalChildren property like this:
public class WaterTextBox : TextBox
{
protected override IEnumerator LogicalChildren
{
get
{
ArrayList list = new ArrayList();
list.Add(WatermarkService.GetWatermark(this));
return (IEnumerator)list.GetEnumerator();
}
}
}
Remember this is just a workaround and this way would work only on your custom textboxes with dynamic resources.
Also it is not the correct implementation because you should add the watermark to the other logical children not ignore the other logical children and have only the watermark which is not even checked for null like this:
public class WaterTextBox : TextBox
{
protected override IEnumerator LogicalChildren
{
get
{
ArrayList list = new ArrayList();
IEnumerator enumerator = base.LogicalChildren;
while (enumerator.MoveNext())
{
list.Add(enumerator.Current);
}
object watermark = WatermarkService.GetWatermark(this);
if (watermark != null && !list.Contains(watermark))
{
list.Add(WatermarkService.GetWatermark(this));
}
return (IEnumerator)list.GetEnumerator();
}
}
}
To make it more general you should define an interface like IWatermark defining a property like IsWaterMarkAdded which will be implemented by your custom TextBox and ComboBox and will be used by the watermark service. The LogicalChildren override will check for the value of this property. This way you can extend functionality for your TextBox and ComboBox but still it is not an extensible solution for any control.

Related

Property in View must match identical property in ViewModel

I am creating a user control (a textbox that only accepts integers). The control has to have properties to specify max/min values and whether to allow negative values etc.). I am using MVVM, in my view I have public properties e.g.
const string EXAMPLE = "Example";
string example;
public string Example
{
get { return example; }
set
{
if (value == example) return;
example = value;
OnPropertyChanged(EXAMPLE);
}
}
These properties are in my View so that someone using the control will be able to easily set them. In my ViewModel I have an identical property, I need these properties to be bound together so that they and their backing fields always have the same value. I hate the code repetition too.
To be honest the whole approach feels wrong and usually that is a good indication that I am approaching the whole thing from the wrong direction or misunderstanding something fundamental.
I have used WPF before but this is a first attempt at a custom control.
The first thing I want to make sure is that you're truly trying to make a CustomControl and not a UserControl. I believe this question basically is the same as yours except worded differently.
A UserControl lends itself to the MVVM pattern way more readily than a CustomControl because you would have a .xaml (and .xaml.cs) file along with a .cs file to serve as the ViewModel. On the other hand, a CustomControl is never done with MVVM, as the visual appearance (view) is defined and overridable via a ControlTemplate.
Since you said you have a View and ViewModel, let's think about how you would achieve the behavior you want with your textbox. Your textbox will have to validate and reject user input outside the range of values you desire. This means your View code-behind has to have properties and logic that control the restrictions in the input values of the textbox defined in your View. You have already violated MVVM here.
When you said you have a View, that makes me think you're writing a UserControl. But your requirements (a custom behavior for textbox) suggest that you really need a CustomControl, for which you do not use MVVM.
If you agree that you need a CustomControl, here's a quick and dirty example:
public class RestrictedTextBox : TextBox
{
public static readonly DependencyProperty MaxValueProperty = DependencyProperty.Register("MaxValue", typeof(int), typeof(RestrictedTextBox), new PropertyMetadata(int.MaxValue));
public RestrictedTextBox()
{
PreviewTextInput += RestrictedTextBox_PreviewTextInput;
}
public int MaxValue
{
get
{
return (int)GetValue(MaxValueProperty);
}
set
{
SetValue(MaxValueProperty, value);
}
}
private void RestrictedTextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
int inputDigits;
RestrictedTextBox box = sender as RestrictedTextBox;
if (box != null)
{
if (!e.Text.All(Char.IsDigit))
{
// Stops the text from being handled
e.Handled = true;
}
else if (int.TryParse(box.Text + e.Text, out inputDigits))
{
if (inputDigits > MaxValue)
e.Handled = true;
}
}
}
}
XAML Usage:
<local:RestrictedTextBox MaxValue="100"></local:RestrictedTextBox>
There is no MVVM in a Custom Control.
Any Control is only in the View layer. So what you need to do is expose relevant DP to a consumer that you have no knowledge of.
In the Custom Control you need to define your Control behavior, how it reacts to change in DP value and what should be available to a consumer. In the default Template you define how you want to display this Control.
The consumer may want to set or get some dp values so he'll have to bind your Custom Control'dp to a property in his ViewModel but that's up to him.

Strategy, State, Singleton... or a Composition thereof?

I am new on stackoverflow and relatively new to WPF.
I've wrapped my head around a half dozen weighty tomes of Patterns and Best Practices (as well as numerous posts here) but cannot seem to find the solution I am looking for.
My Problem: WPF / .Net 4 / C#
I have a text processor (of type Editor E) that can load one Document (of type Document D) at a time (strored as Editor.CurrentDocument). Several UI controls bind to the Document's properties (all Dependency Properties) such as Document.Title, Document.DateLastModification.
Now I want to be able to switch the actual Document instance without having to unhook and re-hook all event handlers. So I guess the Editor.CurrentDocument property must somehow remain its instance while switching its implementation.
I have tried to create a SingleInstanceDocument class that inherits directly from Document and uses the Singleton pattern. But then I cannot find a way to inject any Document instance into the SingleInstanceDocument without having to internally re-map all properties.
Am I somehow being misguided or missing the point here? If the SingleInstanceDocument approach is a viable solution, is there any way I can use reflection to re-map all available dependency properties from the inner Document to the outer SingleInstanceDocument shell automatically?
Thank you very much!
Addendum:
It turned out that the functionality required here was already provided by WPF/.NET out of the box by implementing INotifyPropertyChanged on the CurrentDocument host object. Thus changing the current document caused the UI to update its bound controls appropriately. I'm sorry for all the confusion.
first, learn some basic MVVM pattern. basically in WPF-MVVM just use ObservableCollection and INotifyPropertyChanged interface.
this type of collection implements the observer pattern that notify update to UI(View) when you add/remove or "select" the current item.
//in main ViewModel
private Ducument _currentDocument;
public Document CurrentDocument
{
get { return _currentDocument; }
set
{
_currentDocument = value;
NotifyPropertyChanged("CurrentDocument");
}
}
//stored all loaded documents as collection.
public ObservableCollection<Document> Documents { get; set; }
binding selected - current item.
<ListBox ItemsSource="{Binding Path=Documents}" SelectedItem="{Binding Path=CurrentDocument}" DisplayMemberPath="Title">
<!-- //all Document.Title as listitem -->
</ListBox>
<!--// Editor's View -->
<ContentControl DataContext="{Binding Path=CurrentDocument}"></ContentControl>

Get button style name

I have several styles on Window.Resources that I apply to several buttons with C#. Then I need to change the style but first I need to know what is the current style that is applied to the button I want to change the style.
I can't find the way to get the style name from the button!
Did you try Button.Style property? If explcit setting of style is done using resource Key then you should get the current style of the button using Button.Style propetry otherwise it is a little tricky to gather all Style related information at a control level.
And there are reasons for this. Styles are inherited and could be overriden at distinct element scopes such as App, Window, UserControl, Ancestor UIElements and finally the individual control. So when you access Button.Style property you get a style that was the last immediate style applied to the Button. If the style is BasedOn another Style then Style.BasedOn will give you the parent / base Style. Again if that BasedOn style is derived from another Style we will get that as Style.basedOn.BasedOn... etc.
I hope this makes sense. :-)
I think you are making a mistake in terms of design/architecture if you approach your issue this way. If you need to change styles conditionally you can create UI-elements based on objects which hold the relevant information using data-binding and templating.
That's a good question (+1).
This is just my thought which may not be very accurate. I doubt if it makes sense to get a style for a UI control. Suppose you apply style "style1" to an UI control and then you can set individual attributes like foreground/background.... Now, what would be the style?
If you want to maintain/track the state of the button, that should be handled either as visual states or in your code behind (ViewModel/Model) probably.
See: Style
public void FooFunc()
{
Button myButton = ...;
Console.WriteLine("The Style: {0}", myButton.Style);
}
I think that's what you're looking for?
Thanks for your answers, I'm using this function from another stackoverflow.. it works and returns the style name into a string!
static public string FindNameFromResource(ResourceDictionary dictionary, object resourceItem)
static public string FindNameFromResource(ResourceDictionary dictionary, object resourceItem)
{
foreach (object key in dictionary.Keys)
{
if (dictionary[key] == resourceItem)
{
return key.ToString();
}
}
return null;
}
#Max,
I'm new to WPF, and had to toggle my Border object's style between one of two known Styles it can have. Rather than use the linear search in FindNameFromResource, I instead instead did this ...
Style normal = (Style)this.Resources["NormalBorder"];
Style strong = (Style)this.Resources["StrongBorder"];
border.Style = border.Style == normal ? strong : normal;

Is there an easy/built-in way to get an exact copy (clone) of a XAML element?

I need to make areas of XAML printable and so have make this button handler:
private void Button_Click_Print(object sender, RoutedEventArgs e)
{
Customer.PrintReport(PrintableArea);
}
And in PrintReport I pack the frameworkelement into other elements in order to print it in a slightly different way than it is on the screen, like this:
public void PrintReport(FrameworkElement fwe)
{
StackPanel sp = new StackPanel();
sp.Children.Add(fwe);
TextBlock tb = new TextBlock();
tb.Text = "hello";
sp.Children.Add(tb);
PrintDialog dialog = new PrintDialog();
if (dialog.ShowDialog() == true)
{
dialog.PrintVisual(sp, "Print job");
}
}
But the above gives me the following error:
Specified element is already the
logical child of another element.
Disconnect it first.
Is there an easy way to clone the FrameworkElement so that I can manipulate the copy, print it, and then forget about it, leaving the original element in the XAML being displayed on the screen intact?
Something like this I would imagine:
FrameworkElement fwe2 = FrameworkElement.Clone(fwe); //pseudo-code
I had a similar problem in my current project and solved it with this code.
public static class ExtensionMethods
{
public static T XamlClone<T>(this T original)
where T : class
{
if (original == null)
return null;
object clone;
using (var stream = new MemoryStream())
{
XamlWriter.Save(original, stream);
stream.Seek(0, SeekOrigin.Begin);
clone = XamlReader.Load(stream);
}
if (clone is T)
return (T)clone;
else
return null;
}
}
This way it simply appears as a method on all objects in your WPF project, you do not need to give any parameters to the method, and it returns an object of the same class as the original.
In WPF, copying (or "cloning") elements is almost never correct. This effectively makes this an XY Problem question. I.e. you only think that you need to literally clone the elements in your visual tree. But you don't.
The idiomatic and correct approach here is to declare a DataTemplate that represents the data you want to print. Of course, that also means that the data you want to print is in turn being represented by a view model class, for which the DataTemplate has been declared (i.e. through the DataType property).
For example:
<DataTemplate DataType={x:Type PrintableViewModel}>
<!-- template contents go here -->
</DataTemplate>
The PrintableViewModel class being, of course, a view model class containing the data you want to use to populate the visual tree that will be printed.
In the XAML for your UI, you'd then use it something like this:
<ContentControl Content={Binding PrintableViewModelProperty}/>
I.e. bind the Content property to a property in the current DataContext object that returns an instance of your PrintableViewModel, and let the ContentControl display the data appropriately.
WPF will look up the appropriate data template and apply it for display in the ContentControl. When you want to print the data, you then just do something like this:
PrintDialog printDialog = new PrintDialog();
if (printDialog.ShowDialog() == true)
{
ContentControl contentControl = new ContentControl { Content = ((ViewModelClass)DataContext)PrintableViewModelProperty};
// This part with the margins is not strictly relevant to your question per se,
// but it's useful enough to be worth including here for future reference
PageImageableArea area = printDialog.PrintQueue.GetPrintCapabilities(printDialog.PrintTicket).PageImageableArea;
contentControl.Margin = new Thickness(area.OriginWidth, area.OriginHeight,
printDialog.PrintableAreaWidth - area.ExtentWidth - area.OriginWidth,
printDialog.PrintableAreaHeight - area.ExtentHeight - area.OriginHeight);
// This shows retrieving the data template which is declared using the DataType
// property. Of course, if you simply declare a key and reference it explicitly
// in XAML, you can just use the key itself here.
DataTemplateKey key = new DataTemplateKey(typeof(MazeViewModel));
contentControl.ContentTemplate = (DataTemplate)FindResource(key);
printDialog.PrintVisual(contentControl, "MazeGenerator");
}
This will cause WPF to automatically reuse the template you've already described for the PrintableViewModel class, populating the ContentControl's visual sub-tree according to that template, duplicating the visual you're displaying on the screen, but without having to do any sort of explicit cloning of UI elements.
The above illustrates how to reuse the visual representation exactly. But of course if you have a desire to customize the output for the purpose of printing, it's as simple as declaring a different DataTemplate to be used when you print.
I'm sure there should be easy way to do the copy (other than detaching from parent, printing and attaching back). For example you could try XamlWriter to write xaml, and then read it back via XamlReader. But I suspect there may be some binding and layout errors this way.
Instead I would try to use WriteableBitmap to take a snapshot of printable area and print it. This way you create raster and loose vector, but I'm not good enough in printing to say if it's good or bad. Anyway you could try and check :).
Cheers, Anvaka.

How can I apply MVVM and Commands in this specific WPF situation?

I am having trouble with the MVVM pattern and Commands in my WPF app. The problem is not so much the MVVM pattern, but more the stuff that is going on on my GUI. I'll explain the situation:
My app can DoStuff to some files. I have a class with a function DoStuff(int limit). My user user interface has the following items:
A Button DoStuffBtn to start parsing.
A TextBox LimitTxt to fill in a limit.
A CheckBox LimitChk to enabled or disable the limit.
When you would "uncheck" LimitChk, then LimitTxt.Text = "" and LimitTxt.IsEnabled = false. When you would "check" LimitChk, then LimitTxt.IsEnabled = false again, but the text remains empty until you fill something in.
I have read many tutorials on Commands in WPF and MVVM but I just can't seem to pour my case into that mold. The example I gave is actually just a small part of my UI, but I can't seem to do this nicely either.
I keep running into questions like:
Do I need two Commands for LimitChk (enable and disable) or just one (toggle)?
If I bind an int to LimitTxt, what happens if I make it empty and disable it?
Is it a clean way to just use DoStuff(Int32.Parse(LimitTxt.Text)) when DoStuffBtn is pressed?
If I use two commands on LimitChk, what happens with the CanExecute() function of ICommand that determines whether LimitChk is enabled?
So the main question is: How would the situation I described fit into a nice pattern using Commands in WPF?
Some links on WPF, Commands and MVVM i've looked at:
http://www.devx.com/DevX/Article/37893/0/page/1
http://msdn.microsoft.com/en-us/magazine/cc785480.aspx?pr=blog
http://jmorrill.hjtcentral.com/Home/tabid/428/EntryId/432/MVVM-for-Tarded-Folks-Like-Me-or-MVVM-and-What-it-Means-to-Me.aspx
http://msdn.microsoft.com/en-us/magazine/dd419663.aspx
What I understand so far is that I have to keep as much as possible out of the UI. Even stuff like UI influencing the UI. I.e. unchecking LimitChk disables LimitText. Still, I think I should keep a difference between UI related information and actions and stuff that actually has to do with the actual work that has to be done.
I think you're getting confused... you don't need any commands here, you can just use bindings.
Do I need two Commands for LimitChk (enable and disable) or just one (toggle)?
You need none. Just create a LimitEnabled property in your ViewModel, and bind the CheckBox to it (IsChecked="{Binding LimitEnabled}")
If I bind an int to LimitTxt, what happens if I make it empty and disable it?
Disabling it has no effect. If you make the TextBox empty, the binding will fail because an empty string can't be converted to an int (at least not with the default converter)
Is it a clean way to just use Parse(Int32.Parse(LimitTxt.Text)) when ParseBtn is pressed?
You don't need to. Just create a Limit property in your ViewModel, and bind the TextBox to it. You might want to add an ExceptionValidationRule to the Binding so that it highlights invalid input.
The button is not necessary, the parsing will be done automatically when the TextBox loses focus (if you use the default UpdateSourceTrigger). If you want to customize the way it's parsed, you can create a custom converter to use in the binding.
Just some high level thoughts, leaving out superfluous stuff like Color and alignment attributes, WrapPanels, etc.
Your ViewModel has a a couple properties:
public bool? LimitIsChecked { get; set; }
public bool LimitTextIsEnabled { get; set; } //to be expanded, below
public ICommand ParseCommand { get; private set; } // to be expanded, below
public string LimitValue { get; set; } // further explanation, below
Your XAML has CheckBox and TextBox definitions something like:
<CheckBox Content="Limit Enabled" IsChecked="{Binding LimitIsChecked}" />
<TextBox Text="{Binding LimitValue}" IsEnabled="{Binding LimitIsEnabled}" />
<Button Content="Parse" Command="{Binding ParseCommand}" />
You'll want to initialize ParseCommand something like this:
this.ParseCommand = new DelegateCommand<object>(parseFile);
Now, let's fill in that LimitTextIsEnabled property too:
public bool LimitTextIsEnabled {
// Explicit comparison because CheckBox.IsChecked is nullable.
get { return this.LimitIsChecked == true; }
private set { }
}
Your parseFile method would then pass the value of the LimitValue property to the logic doing the actual parsing.
I declared the LimitValue property as string here to avoid cluttering up the code with an explicit converter, or other validation code. You could choose to handle that "LimitValue is a valid int" verification/conversion in several different ways.
Of course, I haven't implemented this in its entirety, but I wanted to outline a pattern where you are not using Commands to update the state of the other widgets. Instead, bind those attributes to properties that are managed in your ViewModel.

Categories