1

I want a data template to display the usual things except I want to remove (make hidden) certain UI elements when the user is in certain windows. I was wondering how I can get access to a data template to edit it like that. I already know how to get access to the object that the template is displaying (the binding data source), just need the template.

My Add new Machine Window:

private void OnInit(object sender, RoutedEventArgs e)
{
    this.DataContext = new MachineItem("Type your description here",
        MachineTypeEnum.Computer, "1.1.1.1", "1.1.1.1", 4, null, ((GUIApp)Application.Current).CurrentMachineGroup,
        BordersStyle.Blue);

    //Below are 2 lines that sudo represent what I am trying to do.
    var template = this.DataContext as TheTemplateIWant; //Wrong
    template.DeleteButton.Visibility = Visibility.Hidden; //I don't need a delete button on something I am trying to add
}

I know another way I could do this is to change a property of the "MachineItem" object and then make a DataTrigger in the template to edit the UI based on this property (I could use a bool), but this seems a bit hackish because the MachineItem object is representing data and shouldn't have to keep track of which window it is in. I'm also open to other ways other than editing in OnInit(), as long as it is good practice

Goku
  • 1,565
  • 1
  • 29
  • 62

2 Answers2

0

One option is to use different DataTemplate for that window, override it in Window.Resources.

Alternatively, you can find DataTemplate elements in VisualTree after window is rendered/attached to it.

To accomplish this you can find Window elements by type, name. For example, if you have DataTemplate with some elements and button with name

 <DataTemplate DataType="{x:Type local:MachineItem}">
       <StackPanel>
            <TextBlock Text="{Binding Id}"></TextBlock>
            <TextBlock Text="{Binding Name}"></TextBlock>
            <Button x:Name="DeleteButton"> delete</Button>
       </StackPanel>
  </DataTemplate>

You can find DeleteButtons in window using VisualTreeHelper like

VisualTreeHelperExtensions.FindChild<Button>(this, "DeleteButton");

I have modified version of VisualTreeHelper extension found here which returns all elements of type by name

public static class VisualTreeHelperExtensions
{
    public static IEnumerable<T> FindChild<T>(DependencyObject parent, string childName)
        where T : DependencyObject
    {
        // Confirm parent and childName are valid. 
        if (parent == null)
        {
            yield break;
        }

        int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
        for (int i = 0; i < childrenCount; i++)
        {
            var child = VisualTreeHelper.GetChild(parent, i);
            // If the child is not of the request child type child
            T childType = child as T;
            if (childType == null)
            {
                // recursively drill down the tree
                foreach (var innerChild in FindChild<T>(child, childName))
                {
                    yield return innerChild;
                }
            }
            else if (!string.IsNullOrEmpty(childName))
            {
                var frameworkElement = child as FrameworkElement;
                // If the child's name is set for search
                if (frameworkElement != null && frameworkElement.Name == childName)
                {
                    // if the child's name is of the request name
                    yield return (T)child;
                }
            }
            else
            {
                // child element found.
                yield return (T)child;
            }
        }
    }
}

So complete example (with template defined above):

    private void OnInit(object sender, RoutedEventArgs e)
    {
        this.DataContext = new MachineItem("Type your description here",
            MachineTypeEnum.Computer, "1.1.1.1", "1.1.1.1", 4, null, ((GUIApp)Application.Current).CurrentMachineGroup,
            BordersStyle.Blue);

        var buttons = VisualTreeHelperExtensions.FindChild<Button>(this, "DeleteButton");

        foreach (var button in buttons)
        {
            button.Visibility = Visibility.Hidden;
        }
    }
Piwnik
  • 181
  • 1
  • 5
0

You can use FrameworkElement.FindResource to find the "closest" resource, by name. This will search all the <x.Resources> collections, in all the ancestor elements in XAML, including those defined in App.xaml. It will end up resolving the resource exactly same way as if you had used a static resource in the XAML code.

From MSDN:

Searches for a resource with the specified key, and throws an exception if the requested resource is not found.

...

If the resource is not found on the calling element, the parent element in the logical tree is searched next, then the application, then themes, and finally system resources. This lookup methodology is identical to how the tree is searched if a resource were requested by a dynamic resource reference in markup. For more information about resource lookup, see XAML Resources.

Typically, you immediately cast a FindResource return value to the type of the property that you setting with the returned resource value.

Resource keys are not necessarily strings. For instance, styles for controls at the theme level are deliberately keyed to the Type of the control, and application or page styles for controls typically use this same key convention. For details, see Styling and Templating or XAML Resources.

In your example, it would look something like:

var template = this.FindResource("TheTemplateIWant") as DataTemplate;

All of that being said, you would probably be better off using data-binding, and binding the visibility of the element to a property of the view-model, instead of trying to manipulate the template programaticaly.

Bradley Uffner
  • 16,641
  • 3
  • 39
  • 76