-1

I got a problem binding an ItemTemplates Parent context inside an item template.

There are plenty of "workarounds" which only work in WPF (i.e. using FindAncestor and AncestorType). This are out of question, as it's not supported in Windows Store Apps.

Other solutions suggest using ElementName. While this works in Windows Store Apps, it's not an acceptable solution, as it reusable DataTemplates impossible.

One solution I did read about was using Attached Properties/Attached Behaviors, which sounds like the way to go and is a very generic and reusable method. But I couldn't make it work so far.

My current attempt is, to create a global Attached Property and access it in the ItemTemplate.

public class GlobalProperties : DependencyObject
{
    public static object GetParentDataContext(DependencyObject obj)
    {
        return (object)obj.GetValue(ParentDataContextProperty);
    }

    public static void SetParentDataContext(DependencyObject obj, object value)
    {
        obj.SetValue(ParentDataContextProperty, value);
    }

    // Using a DependencyProperty as the backing store for ParentDataContext.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty ParentDataContextProperty =
        DependencyProperty.RegisterAttached("ParentDataContext",
            typeof(object), 
            typeof(GlobalProperties), 
            new PropertyMetadata(null, ParentDataContextChanged));

    private static void ParentDataContextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        SetParentDataContext(d, e.NewValue);
    }
}

And my XAML (simplified by inlining the DataTemplate within the ListViews XAML Code. It will be later stored outside in an DataTemplate.xaml file.

    <ListView 
        my:GlobalProperties.ParentDataContext="{Binding}"
        ItemsSource="{Binding Questions}"
        >
        <ListView.ItemContainerStyle>
            <Style TargetType="ListViewItem">
                <Setter Property="HorizontalContentAlignment" Value="Stretch"></Setter>
                <Setter Property="Margin" Value="0,-1,0,0" />
            </Style>
        </ListView.ItemContainerStyle>
        <ListView.ItemTemplate>
            <DataTemplate>
                <Grid>
                    <Grid Background="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(my:GlobalProperties.ParentDataContext).Site.Styling.TagBackgroundColor}">
                        <!-- Item Related DataBindings -->      
                    </Grid>
                </Grid>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>

Where my is the namespace where my GlobalProperties is defined.

When I have a breakpoint in ParentDataContextChanged it's definitely called with the DataContext. But I can't get the XAML code to work to read it. The Background Xaml Property is always empty and the background always remains white (color saved in the TagBackgroundColor property is red).

Anyone know what's wrong with the code/attempt?

update Also Background="{Binding RelativeSource={RelativeSource Self}, Path=(my:GlobalProperties.ParentDataContext).Site.Styling.TagBackgroundColor}" doesn't work, since that's what there in most website that show attached properties for such a case.

update 2 Example of of the ViewModel for better clarification

public class QuestionsViewModel : ViewModel 
{
    // Site.Styling.BackgroundColor to be bound to certain ItemTemplate 
    // Properties, but don't have access to it from the ItemTemplate
    // because it has the context of one item from Questions
    public Site Site { get; set; };
    // Questions bound to ListView.DataSource > becomes Item's DataContext
    public IEnumerable<Question> Questions { get; set; };
}

public class Site 
{
    public Style Styling { get; set; }
}

public class Style 
{
    public string ForegroundColor { get; set; }
    public string BackgroundColor { get; set; }
    public string LinkColor { get; set; }
}
Tseng
  • 61,549
  • 15
  • 193
  • 205
  • I think, creating a converter with dependency property will work in this case. – Vishal Feb 27 '15 at 05:17
  • Sounds like an interesting approach, but not sure how it's going to work. Dependency Properties are tied to a specific element. Any working examples? – Tseng Feb 27 '15 at 21:11

1 Answers1

0

With reference to above comment, I'm putting sample code.

Main.xaml

<Page
x:Class="TempApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converter="using:TempApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <ListView x:Name="MyList">
        <ListView.ItemContainerStyle>
            <Style TargetType="ListViewItem">
                <Setter Property="HorizontalContentAlignment" Value="Stretch"></Setter>
                <Setter Property="Margin" Value="0,-1,0,0" />
            </Style>
        </ListView.ItemContainerStyle>
        <ListView.ItemTemplate>
            <DataTemplate>
                <Grid x:Name="ListItemDataTemplateGrid"                           
                      HorizontalAlignment="Stretch">
                    <Grid.Resources>
                        <converter:ValueToBackgroundConverter x:Key="ValueToBackgroundConverter" BackgroundColor="{Binding BgColor}" />
                    </Grid.Resources>
                    <Grid Background="{Binding Converter={StaticResource ValueToBackgroundConverter}}">
                        <!--Your Content-->
                    </Grid>
                </Grid>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</Grid>

Main.xaml.cs

public sealed partial class MainPage : Page
{
    public List<TempList> ListDataSource = new List<TempList>();
    public MainPage()
    {
        this.InitializeComponent();
        FillList();           
    }

    private void FillList()
    {
        ListDataSource.Clear();
        ListDataSource.Add(new TempList { BgColor = "Red" });
        ListDataSource.Add(new TempList { BgColor = "Red" });
        MyList.ItemsSource = ListDataSource;
    }
}

public class TempList
{
    public string BgColor { get; set; }
}

ValueToBackgroundConverter.cs

class ValueToBackgroundConverter : DependencyObject, IValueConverter
    {
        public string BackgroundColor
        {
            get { return (string)GetValue(BackgroundColorProperty); }
            set { SetValue(BackgroundColorProperty, value); }
        }

        public static readonly DependencyProperty BackgroundColorProperty =
         DependencyProperty.Register("BackgroundColor",
                                     typeof(string),
                                     typeof(ValueToBackgroundConverter), null
                                     );

        public object Convert(object value, System.Type targetType, object parameter, string language)
        {
//I've used static colors but you can do manipulations to convert string to color brush
            if (BackgroundColor != null)
                return new SolidColorBrush(Color.FromArgb(0xFF, 0xA3, 0xCE, 0xDC));
            else
                return new SolidColorBrush(Color.FromArgb(0xFF, 0xE3, 0xF0, 0xF4));
        }

        public object ConvertBack(object value, System.Type targetType, object parameter, string language)
        {
            throw new System.NotImplementedException();
        }
    }
Vishal
  • 171
  • 7
  • It doesn't exactly address the problem I have. This one binds the Value within the ItemTemplate. I need to bind values which are defined in the ViewMoldel that's bind to the Page. If it were within the Items, I could simply bind it without the converter. I've added ViewModel example to better clarify it – Tseng Mar 02 '15 at 12:36