3

I have a DataTemplate defined as follows

<DataTemplate x:Key="PasswordViewerTemplate">
  <StackPanel>
    <TextBlock Text="{Binding PasswordChar, ElementName=this}"
               Visibility="Visible" />
    <TextBox Text="{Binding PasswordText}"
             Visibility="Collapsed" />
  </StackPanel>
</DataTemplate>

I want to be able to toggle visibilities of the TextBlock and the TextBox each time the user clicks on the StackPanel. I tried setting a MouseLeftButtonUp event handler on the StackPanel but this throws an exception

Object reference not set to an instance of an object

Is there another way to achieve this? Maybe in XAML itself using triggers?

Also, this might be relevant. The above template is one of two that is applied to a ListBox by a template selector. The ListBox itself is within a Grid and both templates are defined within the Grid.Resources section.

EDIT 1
I tried setting the event as follows

<StackPanel MouseLeftButtonUp="OnPasswordViewerMouseLeftButtonUp">
...
</StackPanel>
private void OnPasswordViewerMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
  var sp = sender as StackPanel;
  if( ( sp == null ) || ( sp.Children.Count != 2 ) ) {
    return;
  }

  var passwordText = sp.Children[0] as TextBlock;
  var plainText = sp.Children[1] as TextBox;
  if( ( passwordText == null ) || ( plainText == null ) ) {
    return;
  }

  passwordText.Visibility = ( passwordText.Visibility == Visibility.Visible ) ? 
    Visibility.Collapsed : Visibility.Visible;
  plainText.Visibility = ( plainText.Visibility == Visibility.Visible ) ?
    Visibility.Collapsed : Visibility.Visible;
}
Dave Clemmer
  • 3,741
  • 12
  • 49
  • 72
Praetorian
  • 106,671
  • 19
  • 240
  • 328
  • Do you want to display a `TextBox` instead of a `TextBlock` for the selected item? – Fredrik Hedblad Aug 26 '11 at 08:55
  • @Meleak I want the toggle the visibilities of the `TextBlock` and the `TextBox` each time the user clicks on the `StackPanel`. – Praetorian Aug 26 '11 at 10:58
  • If I understand this correctly, then the `StackPanel` is in the `ItemTemplate` of a `ListBoxItem` so the `TextBlock` will decide the size of the `StackPanel`. This means that you can never click on the `StackPanel` directly since the `TextBlock` will lay on top of it unless you set `HorizontalContentAlignment` to `Stretch` in `ItemContainerStyle`. – Fredrik Hedblad Aug 26 '11 at 11:14
  • What do you want to happend if the `TextBox` is visible and you click it? Should that toggle visibility as well? – Fredrik Hedblad Aug 26 '11 at 11:15
  • @Meleak Yes, the `TextBlock` completely covers the `StackPanel`, but shouldn't the click event bubble up to the `StackPanel` anyway? If you look at the XAML above, initially TextBlock is visible, TextBox is not. First click -> TextBlock collapsed, TextBox visible. Second click -> TextBlock visible, TextBox collapsed. And so on ... – Praetorian Aug 26 '11 at 12:52

2 Answers2

1

One of the solutions is to bind visibility of the TextBox and TextBlock to properties of the class which is used as DataContext for the StackPanel. Here is a sample implementation:

Xaml code:

<Grid>
    <Grid.Resources>
        <DataTemplate x:Key="PasswordViewerTemplate">
            <StackPanel PreviewMouseUp="StackPanel_PreviewMouseUp">
                <TextBlock Text="{Binding Path=PasswordChar}"
           Visibility="{Binding Path=TextBlockVisibility}" />
                <TextBox Text="{Binding Path=PasswordText}"
         Visibility="{Binding Path=TextBoxVisibility}" />
            </StackPanel>
        </DataTemplate>
    </Grid.Resources>
    <ListBox x:Name="lbox" ItemTemplate="{StaticResource ResourceKey=PasswordViewerTemplate}" ItemsSource="{Binding}"/>
</Grid>

And C# code:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        ObservableCollection<Some> items = new ObservableCollection<Some>();
        for (int i = 0; i < 10; i++)
        {
            items.Add(new Some(string.Format("passwordChar {0}", i + 1), string.Format("passwordText {0}", i + 1), Visibility.Visible, Visibility.Collapsed));
        }
        this.lbox.ItemsSource = items;
    }

    private void StackPanel_PreviewMouseUp(object sender, MouseButtonEventArgs e)
    {
        Some some = (sender as StackPanel).DataContext as Some;
        some.TextBlockVisibility = ToggleVisibility(some.TextBlockVisibility);
        some.TextBoxVisibility = ToggleVisibility(some.TextBoxVisibility);
    }
    private Visibility ToggleVisibility(Visibility visibility)
    {
        return visibility == Visibility.Visible ? Visibility.Collapsed : Visibility.Visible;
    }
}
public class Some:INotifyPropertyChanged
{
    private string _passwordChar;
    private string _passwordText;
    private Visibility _textBlockVisibility, _textBoxVisibility;

    public string PasswordChar { get { return this._passwordChar; } set { this._passwordChar = value; } }
    public string PasswordText { get { return this._passwordText; } set { this._passwordText = value; } }
    public Visibility TextBlockVisibility 
    { 
        get { return this._textBlockVisibility; } 
        set 
        { 
            this._textBlockVisibility = value;
            RaisePropertyChanged("TextBlockVisibility");
        }

    }
    public Visibility TextBoxVisibility 
    { 
        get { return this._textBoxVisibility; }
        set 
        { 
            this._textBoxVisibility = value;
            RaisePropertyChanged("TextBoxVisibility");
        }
    }

    public Some(string passwordChar, string passwordText, Visibility textBlockVisibility, Visibility textBoxVisibility)
    {
        this._passwordChar = passwordChar;
        this._passwordText = passwordText;
        this._textBlockVisibility = textBlockVisibility;
        this._textBoxVisibility = textBoxVisibility;
    }


    public event PropertyChangedEventHandler PropertyChanged;
    protected void RaisePropertyChanged(string name)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(name));
    }
}
Dave Clemmer
  • 3,741
  • 12
  • 49
  • 72
makagonov
  • 1,652
  • 1
  • 10
  • 11
0

Why dont you bind the item's visibility in your view model?

an Example.

<Textblock Test="{Binding passwordText,ElementName=This}" Visibility="{Binding passwordTextVisibility}"/>

in your ViewModel say

public Visibility passwordTextVisibility
{
 getters and setters here
}

and on your mouse event, you would need some sort of routed event inside the stack panel. an example:

inside the stack panel you would need mouse. whatever you need. read a little about routed events

Example. if PreviewMouseLeftButtonUp does not work.

<StackPanel Mouse.MouseUp="MouseButtonUpEventHandler"/>

in the view model

public void MouseButtonUpEventHandler (RoutedEvent e)
{
//logic here to check if it's left mouse if it is then set visibility
}

}

Dave Clemmer
  • 3,741
  • 12
  • 49
  • 72
Kevin
  • 3,509
  • 4
  • 31
  • 45
  • And how would I tie this to the mouse click event? – Praetorian Aug 26 '11 at 03:35
  • I think there is a PreviewMouseLeftButtonUp you can use – Kevin Aug 26 '11 at 03:41
  • and when your mouseLeftbutton comes up, then you set the visibility – Kevin Aug 26 '11 at 03:41
  • heres a good link http://msdn.microsoft.com/en-us/library/system.windows.input.mouse.previewmouseup.aspx – Kevin Aug 26 '11 at 03:43
  • Setting an event handler for PreviewMouseLeftButtonUp throws the same exception (same for Mouse.PreviewMouseUp). Anyway, if I need to set an event handler I can just get the StackPanel, its Children and then toggle the Visibility in code rather than messing with the view model. – Praetorian Aug 26 '11 at 03:45
  • I've always learned its a bad idea to do it in the code behind, but toggling the visibility isn't bad. are you sure you are writing the events correctly? post more code i'll ty to help you the best i can – Kevin Aug 26 '11 at 03:57
  • Can you try Mouse.MouseUp? inside the xaml rather using MouseLeftButtonUp – Kevin Aug 26 '11 at 04:07
  • and do private void ButtonOkClicked(object sender, RoutedEventArgs e) { Mouse mouseUp= (Mouse) sender; //logic } – Kevin Aug 26 '11 at 04:11
  • Same thing, I think there's another underlying issue here with the DataTemplate that prevents me from setting any event handler. Maybe because it is being selected by a DataTemplateSelector ... – Praetorian Aug 26 '11 at 04:19