1

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

Community
  • 1
  • 1
saamorim
  • 3,855
  • 17
  • 22
  • From your description, it sound more like a problem with your `Watermark` code... can you show that? – Sheridan Sep 09 '13 at 11:38
  • Thx. Like said in the post, the code is avaiable at http://stackoverflow.com/questions/833943/watermark-hint-text-textbox-in-wpf in the post of John Myczek – saamorim Sep 09 '13 at 12:01

1 Answers1

1

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.

Chevul Ervin
  • 863
  • 5
  • 9
  • You're right since Textblock.Parent is null, but how do I set it right since at line 3 of the construtor there is already a `this.contentPresenter.Content = watermark;` where watermark is "the" TextBox – saamorim Sep 09 '13 at 12:07
  • Still the textblock can have a different logical parent. The logical and visual trees are different. – Chevul Ervin Sep 09 '13 at 12:50
  • Thank you. I'll check your solution and see if this fits. Didn't want to have custom controls for this, but I'll check anyway. If you remember anyother way to do this, I'll be glad if you let us know. Regards – saamorim Sep 09 '13 at 13:56
  • You could easily get away just be putting your resources higher in the hierarchy for example in the application resources (ex. in App.xaml). – Chevul Ervin Sep 09 '13 at 14:10
  • Thx. Currently they are in the shell (Main Window), I'll try to move them just one step upper. – saamorim Sep 09 '13 at 15:17
  • Excellent!! This last solution (moving the resource to the app level) solved all my problems and was so simple!! Even for other bugs that were behaving the same way! – saamorim Sep 09 '13 at 15:24