Привязка родительского DataContext из шаблона ItemTemplate в приложениях Магазина Windows

У меня проблема с привязкой родительского контекста ItemTemplates к шаблону элемента.

Существует множество "обходных путей", которые работают только в WPF (т.е. с использованием FindAncestor а также AncestorType). Это не подлежит сомнению, поскольку не поддерживается в приложениях Магазина Windows.

Другие решения предлагают использовать ElementName, Хотя это работает в приложениях Магазина Windows, это неприемлемое решение, так как повторное использование DataTemplates невозможно.

Одним из решений, о котором я читал, было использование Attached Properties/Attached Behaviors, которое звучит как путь, и это очень общий и многократно используемый метод. Но я не мог заставить это работать до сих пор.

Моя текущая попытка состоит в том, чтобы создать глобальное присоединенное свойство и получить к нему доступ в 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);
    }
}

И мой XAML (упрощается путем встраивания DataTemplate в XAML-код ListViews. Позже он будет сохранен снаружи в файле DataTemplate.xaml.

    <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>

куда my это пространство имен, где мой GlobalProperties определено.

Когда у меня есть точка останова в ParentDataContextChanged это определенно вызывается с DataContext. Но я не могу заставить работать код XAML для его чтения. Background Свойство Xaml всегда пустое, а фон всегда остается белым (цвет сохранен в TagBackgroundColor свойство красное).

Кто-нибудь знает, что не так с кодом / попыткой?

ТакжеобновитьBackground="{Binding RelativeSource={RelativeSource Self}, Path=(my:GlobalProperties.ParentDataContext).Site.Styling.TagBackgroundColor}" не работает, так как это то, что есть на большинстве веб-сайтов, которые показывают прикрепленные свойства для такого случая.

Обновление 2 Пример ViewModel для лучшего разъяснения

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; }
}

1 ответ

Со ссылкой на комментарий выше, я выкладываю пример кода.

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();
        }
    }
Другие вопросы по тегам