1

ProfileUser の EntityCollection を持つプロファイルがあります。このクラスには、Profile_ID と Profile リレーションと User_ID がありますが、User は別のデータベースにあるため、User_ID はありません。

Datagrid で User.Username 経由でアクセスしたい

私はこれを試しましたが、うまくいきません...

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

もちろん、基本クラスで派生クラスを構築することはできません。しかし、この Operator をバインド先のコレクションの一部にする必要があります...

あなたが私の問題を理解し、助けてくれることを願っています。

編集: このコンバーターは私の問題を解決しました:

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

必要なバインディングと同様のバインディングを実現する 3 つの方法を次に示します。

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) 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) 3 番目の最も複雑ですが、ラッパーなしで完全に動作するのは、添付プロパティ、コンバーター、および 2 つのバインディングに基づいていますが、2 つの添付プロパティと 1 つの変更コールバックでも実行できます。私は前者の方法を好むので、ここにあります:

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

EDIT:これは非常に良い解決策ですが、ターゲットデータオブジェクトがDependencyObjectから継承する必要があることが判明しましたが、そうではありません。ここで、データ オブジェクトはおそらく EntityObject から継承され、ソース DAO オブジェクトであるため、これを変更することはできません:)

そうは言っても、添付プロパティで遊んでみましょう。


Attached Propertiesを使用すると、非常に簡単に解決できる場合があります。バインディング エンジンは、通常のプロパティまたは依存関係プロパティにバインドできるだけではありません。実際には 5 つ以上のものにバインドできますが、その中で最も簡単なのは Att.Props です。

添付プロパティは、「仮想」またはむしろ「偽」プロパティ (「IsZonk」と名付けます) であり、ランダムなクラス (ABC と呼びます) で定義されていますが、バインディング エンジンがそれをあたかもそれであるかのように扱うように特別な方法で登録されています。指定したターゲット クラス (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 に答える