繁体   English   中英

将自定义用户控件的StackPanel绑定到自定义类的ObservableCollection

[英]Binding a StackPanel of custom user controls to an ObservableCollection of custom classes

我有一个名为NextBestActions的ObservableCollection'<'NextBestAction'>'NextBestActions,其中NextBestAction是:

[TypeConverter(typeof(NextBestActionTypeConverter))]
public class NextBestAction : IDisposable
{
    public bool isDismissable, dismissed, completed;
    public NextBestActionType type;
    public string title, description;

    public void Dispose()
    {
        this.Dispose();
    }

    public NextBestAction()
    {

    }

    public NextBestAction(string title, string description)
    {
        this.title = title;
        this.description = description;
    }

    public static NextBestAction Parse(Card card)
    {
        if (card == null)
        {
            return new NextBestAction();
        }

        return new NextBestAction(card.Title.Text, card.Description.Text);
    }
}

我也有自己的UserControl,称为Card,其中Card是:

public partial class Card : UserControl
{
    public Card()
    {
        InitializeComponent();
        this.DataContext = this;
    }

    public Card(string title, string description)
    {
        InitializeComponent();
        this.DataContext = this;
        this.Title.Text = title;
        this.Description.Text = description;
    }

    public static Card Parse(NextBestAction nextBestAction)
    {
        if (nextBestAction == null)
        {
            return new Card();
        }

        return new Card(nextBestAction.title, nextBestAction.description);
    }
}

与以下XAML:

<UserControl x:Class="AdvancedTeller.Card"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:local="clr-namespace:AdvancedTeller"
         mc:Ignorable="d" 
         d:DesignWidth="300" Background="White" BorderBrush="#FF333333" VerticalContentAlignment="Top" Width="400">
<Grid Margin="10" VerticalAlignment="Top">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>
    <TextBlock Name="Title" Grid.Column="1" Grid.Row="0" FontSize="18.667" Margin="3"/>
    <TextBlock Name="Description"  Grid.Column="1" Grid.Row="1" VerticalAlignment="Top" TextWrapping="Wrap" Margin="3"/>
</Grid>

最后,我为NextBestAction定义了一个TypeConverter,

public class NextBestActionTypeConverter : TypeConverter
{
    // Override CanConvertFrom to return true for Card-to-NextBestAction conversions. 
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        if (sourceType == typeof(Card))
        {
            return true;
        }

        return base.CanConvertFrom(context, sourceType);
    }

    // Override CanConvertTo to return true for NextBestAction-to-Card conversions. 
    public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
    {
        if (destinationType == typeof(Card))
        {
            return true;
        }

        return base.CanConvertTo(context, destinationType);
    }

    // Override ConvertFrom to convert from a Card to an instance of NextBestAction. 
    public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
    {
        Card card = value as Card;

        if (card != null)
        {
            try
            {
                return NextBestAction.Parse(card);
            }
            catch (Exception e)
            {
                throw new Exception(String.Format("Cannot convert '{0}' ({1}) because {2}", value, value.GetType(), e.Message), e);
            }
        }

        return base.ConvertFrom(context, culture, value);
    }

    // Override ConvertTo to convert from an instance of NextBestAction to Card. 
    public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
    {
        if (destinationType == null)
        {
            throw new ArgumentNullException("destinationType");
        }

        //Convert Complex to a string in a standard format.
        NextBestAction nextBestAction = value as NextBestAction;

        if (nextBestAction != null && this.CanConvertTo(context, destinationType) && destinationType == typeof(Card))
        {
            try
            {
                return Card.Parse(nextBestAction);
            }
            catch (Exception e)
            {
                throw new Exception(String.Format("Cannot convert '{0}' ({1}) because {2}", value, value.GetType(), e.Message), e);
            }
        }

        return base.ConvertTo(context, culture, value, destinationType);
    }
}

我试图将NextBestActions绑定到StackPanel,并强制将NextBestActions在卡中表示为UI。

到目前为止,我已经了解到至少需要这个

<StackPanel Grid.Column="1" Grid.Row="1" Grid.RowSpan="2" Margin="50" >
    <ItemsControl Name="NextBestActionItems" ItemsSource="{Binding NextBestActions}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <AdvancedTeller:Card />
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</StackPanel>

该代码可以编译并运行,没有任何问题,并且为ObservableCollection中的每个项目创建了一个Card并在StackPanel中可见,但是,每个Card的Title和Description为空白,并且不会获取其各自NextBestAction的数据。

我觉得我已经90%了。 我将不胜感激任何帮助。 谢谢!

更新/编辑1:当前未调用/命中NextBestActionTypeConverter。 如果我从XAML中删除ItemsControl.ItemTemplate定义,则将调用NextBestActionTypeConverter,但destinationType为“字符串”。 我试图强制/设置ItemsControl以了解itesm将被表示为Cards。

UPDATE / EDIT 2(答案):以下是答案的摘录:

// Override ConvertTo to convert from an instance of NextBestAction to Card. 
    public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
    {
        if (destinationType == null)
        {
            throw new ArgumentNullException("destinationType");
        }

        //Convert Complex to a string in a standard format.
        NextBestAction nextBestAction = value as NextBestAction;

        if (nextBestAction != null && this.CanConvertTo(context, destinationType) && destinationType == typeof(Card))
        {
            try
            {
                return new Card();
            }
            catch (Exception e)
            {
                throw new Exception(String.Format("Cannot convert '{0}' ({1}) because {2}", value, value.GetType(), e.Message), e);
            }
        }

        return base.ConvertTo(context, culture, value, destinationType);
    }

public partial class Card : UserControl
{
    public Card()
    {
        InitializeComponent();
    }
}

<UserControl x:Class="AdvancedTeller.Card"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:local="clr-namespace:AdvancedTeller"
         mc:Ignorable="d" 
         d:DesignWidth="300" Background="White" BorderBrush="#FF333333" VerticalContentAlignment="Top" Width="400">
<Grid Margin="10" VerticalAlignment="Top">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>
    <TextBlock Name="Title" Grid.Column="1" Grid.Row="0" FontSize="18.667" Margin="3" Text="{Binding Title}"/>
    <TextBlock Name="Description"  Grid.Column="1" Grid.Row="1" VerticalAlignment="Top" TextWrapping="Wrap" Margin="3" Text="{Binding Description}"/>
</Grid>

我可以看到您的代码中有一些潜在的问题点。

首先,看起来您没有使用get; set;完整属性get; set; get; set; 访问器方法,在NextBestAction对象中为TitleDescription定义。

如果我没记错的话,WPF的绑定系统需要具有get / set访问器的完整属性,并且不会绑定到没有它们的字段。

所以

public string title, description;

应该成为

public string Title { get; set; }
public string Description { get; set; }

另一个潜在的问题可能是您尚未在Card UserControl中绑定Title / Description文本框的.Text属性。

因此,假设您没有使用基于Caliber Micro之类的.Name属性自动创建绑定的框架,则此代码

<TextBlock Name="Title" ... />
<TextBlock Name="Description" ... />

应该

<TextBlock Name="Title" Text="{Binding Title}" ... />
<TextBlock Name="Description" Text="{Binding Description}" ... />

另外,我很确定绑定是区分大小写的,因此您要确保绑定的大小写(如果使用Caliburn Micro,则为Name属性)与属性的大小写匹配。

最后,每当我看到UserControl硬编码是.DataContext ,就会在.DataContext中发出警告。 UserControls不应该这样做。 他们应该从使用控件的任何代码获取DataContext,并且不应创建自己的代码。 删除以下代码行,这些代码行对Card UserControl中的.DataContext进行硬编码,然后它将使用NextBestActions集合中的任何值。

public Card()
{
    InitializeComponent();
    this.DataContext = this; // Bad!
}

public Card(string title, string description)
{
    InitializeComponent();
    this.DataContext = this; // Bad!
    this.Title.Text = title; // Should be replaced with bindings as above
    this.Description.Text = description; // Should be replaced with bindings as above
}

(作为旁注,我不知道您正在使用那个TypeConverter做什么:)它通常用于将一种类型转换为另一种类型,例如将字符串"Red"更改为SolidColorBrush ,并且当您将.Color设置为Red时。键入类似<StackPanel Background="Red" /> 我认为您当前的代码中没有使用它,除非您出于某些特定原因需要使用它,否则建议您完全删除它。)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM