1

我有一个带有 ProfileUser EntityCollection 的配置文件。在这个类中,我有一个 Profile_ID 和一个 Profile 关系和一个 User_ID 但没有 USer 关系,因为 User 在另一个数据库中。

在我想通过 User.Username 访问的 Datagrid

我试过这个,但是ofc它不起作用......

public EntityCollection<ProfileUser> ProfileUsers
    {
        get
        {
            if (profile != null) return profile.ProfileUser;
            else return null;
        }
        set
        {
            profile.ProfileUser = value;
        }
    }

这里是我的自定义类

public class ProfileUserExtended : ProfileUser
{
    public Operator User
    {
        get
        {
            return OperatorManager.GetByGuId(this.User_ID);
        }
    }
}

当然我不能通过基类来构造派生类。但我需要这个运算符成为我绑定到的集合的一部分......

我希望你能理解我的问题并能提供帮助。

编辑: 这个转换器为我解决了问题:

public class OperatorConverter:IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        try
        {
            if (!(value is Guid)) return null;
            if (!(parameter is string)) return null;

            var guid = (Guid)value;
            var par = (string)parameter;

            var op = OperatorManager.GetByGuId(guid);
            if (op == null) return null;

            var prop = op.GetType().GetProperty(par);
            if (prop == null) return null;

            return prop.GetValue(op, null);
        }
        catch (Exception e)
        {
            throw (e);
        }
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new System.NotImplementedException();

    }
}

在 XAML 中:

<DataGridTextColumn Header="Name" Binding="{Binding Path=User_ID,Converter={StaticResource ResourceKey=operatorConverter},ConverterParameter='Name'}" IsReadOnly="True" />
4

2 回答 2

2

以下是实现与您需要做的类似的多个绑定的三种方法。

1)一个包装类:

public class ProfileUserWrapper : DependencyObject
{
    public ProfileUserWrapper(ProfileUser thebrain) { TheUser = thebrain; }

    public ProfileUser TheUser { get; private set; }

    public Operator User { get { if (_user != null)return _user; return _user = OperatorManager.GetByGuId(TheUser.User_ID); } }
    private Operator _user = null;
}

现在,public EntityCollection<ProfileUser> ProfileUsers您可以公开例如IEnumerable<ProfileUserWrapper>

public EntityCollection<ProfileUser> ProfileUsers // your original code
{
    get{ if (profile != null) return profile.ProfileUser; else return null;}
    set { profile.ProfileUser = value; }
}

public IEnumerable<ProfileUserWrapper> ProfileUsers2
{
    get { return ProfileUsers.Select(user => new ProfileUserWrapper(user));
}

然后绑定到 ProfileUsers2,并且您的一些绑定应该从“Address”更改为“TheUser.Address”,但这几乎肯定会起作用。

2)第二个,智能转换器,例如:

public class OperatorPicker : IValueConverter
{
    public object Convert(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        var pu = value as ProfileUser;
        if (pu != null)
            return OperatorManager.GetByGuId(pu.User_ID);
        return null;
    }

    public object ConvertBack(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new System.NotImplementedException();
    }
}

写起来几乎再简单不过了。现在您可以在 XAML 绑定中使用转换器:

<Window.Resources>
    <myextra:OperatorPicker x:Key="conv1" />
</Window.Resources>

<Grid>
    <ListBox x:Name="lbxFirst" ItemsSource="{Binding ProfileUsers}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal">
                    <TextBlock Margin="5" Text="{Binding User_ID}" />
                    <TextBlock Margin="5" Text="{Binding Login}" />
                    <TextBlock Margin="5" Text="{Binding Address}" />
                    <TextBlock Margin="5" Text="{Binding Path=., Converter={StaticResource conv1}}" />
                </StackPanel>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</Grid>

但是这样一来,你就会得到Operator它自己的对象。允许您绑定到以这种方式返回的运算符的属性将非常困难,因为 Binding 的路径已经固定在“。”,并且您无法更改它,因为必须向 Converter 传递 ProfileUser 实例。

3) 第三,最复杂但完全没有任何包装器的工作是基于附加属性、转换器和两个绑定,但您也可以在两个附加属性和一个更改回调上执行此操作。我更喜欢前一种方式,所以这里是:

public class DummyClass : DependencyObject
{
    public static readonly DependencyProperty TheOperatorProperty = DependencyProperty.RegisterAttached(
        "TheOperator", typeof(Operator), typeof(DummyClass),
        new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender)
    );

    public static Operator GetTheOperator(DependencyObject elem) { return (Operator)elem.GetValue(TheOperatorProperty); }
    public static void SetTheOperator(DependencyObject elem, Operator value) { elem.SetValue(TheOperatorProperty, value); }
}

    ... xmlns:myextra="clr-namespace:...." ....

<Window.Resources>
    <myextra:OperatorPicker x:Key="conv1" />
</Window.Resources>

<Grid>
    <ListBox x:Name="lbxFirst" ItemsSource="{Binding ProfileUsers}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <StackPanel x:Name="aparent" Orientation="Horizontal"
                            myextra:DummyClass.TheOperator="{Binding Path=., Converter={StaticResource conv1}}">
                    <TextBlock Margin="5" Text="{Binding User_ID}" />
                    <TextBlock Margin="5" Text="{Binding Login}" />
                    <TextBlock Margin="5" Text="{Binding Address}" />
                    <TextBlock Margin="5"
                               Text="{Binding Path=(myextra:DummyClass.TheOperator).OperatorCodename, ElementName=aparent}" />
                </StackPanel>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</Grid>

请注意,在 StackPanel 级别有一个新的绑定。这个可以放在数据模板中的任何地方,甚至是文本框本身请注意它如何通过转换器将 ProfileUser 转换为 Operator。Path=.不是必需的,但我添加了它以便显示绑定的确切含义。请注意最后一个文本框绑定是如何指定的:它是对附加属性的绑定(通过元素名称),而不是对原始数据的绑定!

这次我测试了所有东西,它在我这边工作,在 ProfileUser 上没有 DependencyObject 继承。DummyClass它对继承感到满意。如果你尝试这个,请像往常一样给我留言:)

于 2012-08-14T16:33:52.587 回答
1

编辑:这是一个很好的解决方案,但事实证明它要求目标数据对象从DependencyObject继承,但事实并非如此。在这里,数据对象可能继承自 EntityObject 并且无法更改,因为它是源 DAO 对象:)

话虽如此,让我们玩一下附加属性:


有时您可以使用Attached Properties很容易地解决它。绑定引擎不仅可以绑定到普通属性或依赖属性。实际上它可以绑定到 5 个或更多的东西,其中最简单的一个是 Att.Props。

附加属性是在某个随机类(称为 ABC)中定义的“虚拟”或更确切地说是“假”属性(我们将其命名为“IsZonk”),但以特殊方式注册,以便绑定引擎将其视为出现在您指定的目标类上(比如说 XYZ)。任何通过绑定访问 XYZ 上的 IsZonk 的尝试都会导致绑定引擎将请求退回到 ABC 类。此外,在 ABC 类中生成的方法调用将获得请求源自的确切 XYZ 对象。

通过这种方式,您可以轻松地使用新数据甚至新功能扩展现有对象,甚至无需修改它们。这很像 3.5 版本中添加到 C# 中的“静态扩展方法”。

namespace Whatever {

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        lbxFirst.DataContext = new MyModel();
    }
}

public class MyModel
{
    public IEnumerable<ProfileUser> ProfileUsers
    {
        get
        {
            var tmp = new[]
            {
                new ProfileUser{ User_ID = "001", Login = "Adelle", Address = "123 Shiny Street" },
                new ProfileUser{ User_ID = "002", Login = "Beatrice", Address = "456 Sleepy Hill" },
                new ProfileUser{ User_ID = "003", Login = "Celine", Address = "789 Rover Dome" },
            };

            tmp[0].SetValue(ProfileUserExtras.UserProperty, new Operator { RelatedUser = tmp[0], OperatorCodename = "Birdie", PermissionLevel = 111 });
            tmp[1].SetValue(ProfileUserExtras.UserProperty, new Operator { RelatedUser = tmp[1], OperatorCodename = "Twin", PermissionLevel = 222 });
            tmp[2].SetValue(ProfileUserExtras.UserProperty, new Operator { RelatedUser = tmp[2], OperatorCodename = "Trident", PermissionLevel = 333 });

            return tmp;
        }
    }
}

public class ProfileUser : DependencyObject
{
    public string User_ID { get; set; }
    public string Login { get; set; }
    public string Address { get; set; }
    //- Operator User {get{}} -- does NOT exist here
}

public class Operator
{
    public ProfileUser RelatedUser { get; set; }
    public string OperatorCodename { get; set; }
    public int PermissionLevel { get; set; }
}

public static class ProfileUserExtras
{
    public static readonly DependencyProperty UserProperty = DependencyProperty.RegisterAttached(
        "User",             // the name of the property
        typeof(Operator),   // property type
        typeof(ProfileUserExtras), // the TARGET type that will have the property attached to it

        new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender) // whatever meta you like
    );
}

}

最后一件事是在 XAML 中使用它:

.... xmlns:myextra="clr-namespace:Whatever" ....

<ListBox x:Name="lbxFirst" ItemsSource="{Binding ProfileUsers}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <TextBlock Margin="5" Text="{Binding User_ID}" />
                <TextBlock Margin="5" Text="{Binding Login}" />
                <TextBlock Margin="5" Text="{Binding Address}" />
                <TextBlock Margin="5" Text="{Binding Path=(myextra:ProfileUserExtras.User).OperatorCodename}" />
                <TextBlock Margin="5" Text="{Binding Path=(myextra:ProfileUserExtras.User).PermissionLevel}" />
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

请注意绑定中附加属性的表示法。它必须用括号括起来,而且几乎总是需要一个命名空间前缀,否则绑定引擎会在其他地方寻找其他东西。通过添加括号,您表明此属性是“附加属性”而不是普通属性。

于 2012-08-14T08:58:49.480 回答