3

I need to add Watermarks to my WPF application and I am using this watermark service to achieve it.

It works just as expected, but I need to localize the text showed as watermark and I can't find a way to achieve it.

My xaml declaration:

<TextBox
    Text="{Binding Name, Mode=TwoWay}">
    <Watermark:WatermarkService.Watermark>
        <TextBlock
            Text="{Binding <What-goes-here?>}"
        />
    </Watermark:WatermarkService.Watermark>
</TextBox>

My ViewModel:

public class PersonViewModel
{
    public string Name {get;set;}

    public string NameWatermark {get;set;}
}

Thw following lines do NOT work:

Text="{Binding NameWatermark}"

<Window x:Name="ThisWindow"
...
Text="{Binding Path=NameWatermark,
               ElementName=ThisWindow}"

Is there any way to achieve it?

Any help would be appreciated, thanks.

Community
  • 1
  • 1
JoanComasFdz
  • 2,911
  • 5
  • 34
  • 50

2 Answers2

3

I realize this question is ancient, but for google time-travelers, i hit the exact same problem and solved it by using a MultiDataTrigger to hide/show the watermark element. It works because it's not embedding the element within an adorner like the original Watermark:WatermarkService.Watermark does.

<Grid>
    <TextBox x:Name="messageBox" Text="{Binding Message, UpdateSourceTrigger=PropertyChanged}" Padding="5">
        <TextBox.InputBindings>
            <KeyBinding Key="Enter" Command="{Binding SendMessageCommand}"/>
        </TextBox.InputBindings>
    </TextBox>
    <TextBlock Text="{text:Translate Key=WriteMessageWatermark}" IsHitTestVisible="False" Foreground="LightGray" Margin="7,6,0,0">
        <TextBlock.Style>
            <Style TargetType="TextBlock">
                <Setter Property="Visibility" Value="Collapsed" />
                <Style.Triggers>
                    <MultiDataTrigger>
                        <MultiDataTrigger.Conditions>
                            <Condition Binding="{Binding ElementName=messageBox, Path=Text.IsEmpty}" Value="True" />
                            <Condition Binding="{Binding ElementName=messageBox, Path=IsFocused}" Value="False" />
                        </MultiDataTrigger.Conditions>
                        <Setter Property="Visibility" Value="Visible" />
                    </MultiDataTrigger>
                </Style.Triggers>
            </Style>
        </TextBlock.Style>
    </TextBlock>
</Grid>
jv_
  • 1,784
  • 1
  • 16
  • 12
0

I think the issue is that the WatermarkService is just a static class and no dependencyobject and therefore doesn't inherit the DataContext properly to its content.

At first: Do you really need to bind to the ViewModel? I assume you want to localize more parts of your UI so moving this stuff to a dedicated service would be an option.

Something like this:

<Watermark:WatermarkService.Watermark>
  <TextBlock Text="{Binding Source={x:Static Watermark:LocalizationService.Instance}, Path=[123],FallbackValue='No LocalizationService found'}"/>
</Watermark:WatermarkService.Watermark>

Singleton class for Localization:

    private static volatile LocalizationService _instance;
    private static object syncRoot = new Object();

    public static LocalizationService Instance
    {
      get
      {
        if (_instance == null)
        {
          lock (syncRoot)
          {
            if (_instance == null)
              _instance = new LocalizationService();
          }
        }

        return _instance;
      }
    }

    public string this[int id]
    {
      get
      {
        // do localization stuff here ... 
        return "Localized Value " + id;
      }
    }
  }

If you really want to bind to the window's DataContext you can use the following workaround by assigning the ViewModel to an ObjectDataprovider and access it in your Binding:

  <Grid Loaded="Grid_Loaded">
    <Grid.Resources>
      <ObjectDataProvider x:Key="PersonViewModelProvider" ObjectInstance="{x:Null}" IsAsynchronous="True"/>
    </Grid.Resources>
    <TextBox Text="{Binding Name, Mode=TwoWay}">
      <Watermark:WatermarkService.Watermark>
        <TextBlock Text="{Binding Source={StaticResource PersonViewModelProvider}, Path=NameWatermark}"/>
      </Watermark:WatermarkService.Watermark>
    </TextBox>
  </Grid>

Code Behind:

private void Grid_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
  var panel = sender as Panel;
  if (panel != null)
  {
    var objDataProvider = panel.Resources["PersonViewModelProvider"] as ObjectDataProvider;
    objDataProvider.ObjectInstance = panel.DataContext;
  }
}
SvenG
  • 5,155
  • 2
  • 27
  • 36
  • Thanks for your response. I am currently using this library to localize in WPF: http://www.codeproject.com/Articles/249369/Advanced-WPF-Localization, but I find no way to localize using it. That' why I am binding to the ViewModel (which is getting the Watermkar value from the Resources.resx file). Maybe I can adapt LocExtension class to apply your first option. – JoanComasFdz Mar 15 '12 at 09:29