如何组合两个ObservableCollection来绑定动态按钮

时间:2013-12-04 15:49:20

标签: c# wpf data-binding mvvm

这是使用C#和MVVM。

目前,我通过绑定ObservableCollection< Module>创建了一组按钮。名为ModuleCollection。

模块定义为:

public string ModuleName { get; private set; }
public string ModuleAbbreviation { get; private set; }
public bool ModuleDisabled { get; private set; }
public DateTime ModuleLicenseDate { get; private set; }

每个按钮下方的标签设置为ModuleName,按钮的Content属性设置为ModuleAbbreviation。

我还有一个current_user对象,它包含一个ObservableCollection< UserModule>称为UserModules UserModule定义为:

public int Module_ID { get; set; }
public int User_Module_Access { get; set; } 

使用ModuleCollection和current_user.UserModules列表,我想在以下场景下启用按钮:

  • 如果Module.Disabled = false
  • 和Module.ModuleLicenseDate>现在()
  • 和UserModule.User_Module_Access> 0

否则该按钮将被禁用。

需要注意的其他事项是:UserModules可能只有ModuleCollection的子集,而ModuleCollection将是静态的,但UserModules中的属性将不时刷新。

我的问题是:如何绑定这两个集合,以便我可以创建我的按钮并根据这两个集合设置IsEnabled属性?

[编辑] 2013-12-07

<Button.IsEnabled>
    <MultiBinding Converter="{Binding viewmodel:IsEnabledMultiValueConverter}">
        <Binding Source="{Binding ModuleID}" />
        <Binding Source="{Binding ModuleDisabled}" />
        <Binding Source="{Binding ModuleLicenseDate}" />
        <Binding Source="{Binding current_user.user_modules}" />
    </MultiBinding>
</Button.IsEnabled>

[EDIT2] 2013-12-09

当我更改用户模块中的模块访问级别时,事件将被触发。我将值从accessible(1)更改为unaccessible(0),因此按钮应该被禁用,但是这不会发生。

public class UserModule : INotifyPropertyChanged
{
    public UserModule(int Module_ID, int User_Module_Access)
    {
        this.Module_ID = Module_ID;
        this.User_Module_Access = User_Module_Access;
    }

    private int _userModuleAccess;

    public int Module_ID { get; set; }

    public int User_Module_Access
    {
        get { return _userModuleAccess; }
        set
        {
            _userModuleAccess = value;
            MessageBox.Show("User_Module_Access");
            RaisePropertyChanged("User_Module_Access");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void RaisePropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));

    }
}

我的想法是,ObservableCollection的事件&lt; UserModule&gt;需要在项属性更改时发生。我已经读过ObservableCollections不这样做,只添加,删除和移动项目。怎么做?

User类中的一些内容?

public class User
{
    public string UserName { get; set; }
    public ObservableCollection<UserModule> UserModules = new ObservableCollection<UserModule>();
}


[编辑3] 2013-12-10重做 - 实施ItemsObservableObservableCollection
Launcher.XAML

<Button Content="{Binding ModuleAbbreviation}" 
        Style="{DynamicResource LauncherButton}"
        Background="{Binding ModuleColor}" 
        FontSize="32" FontFamily="Tahoma" Width="130" Height="100" 
        Command="{Binding DataContext.LaunchCommand, RelativeSource={RelativeSource AncestorType=Window}}"
        CommandParameter="{Binding ModuleName}">
    <Button.Resources>
        <viewmodel:IsEnabledMultiValueConverter x:Key="converter" />
    </Button.Resources>
    <Button.IsEnabled>
        <MultiBinding Converter="{StaticResource converter}">
            <Binding Path="ModuleID" />
            <Binding Path="ModuleEnabled" />
            <Binding Path="ModuleLicenseDate" />
            <Binding ElementName="gridModules" Path="DataContext.CurrentUser" />
        </MultiBinding>
    </Button.IsEnabled>
</Button>

LauncherViewModel.cs

class LauncherViewModel
{
    public LauncherViewModel()
    {
        timer = new Timer(SetModuleAccess, null, TimeSpan.Zero, TimeSpan.FromSeconds(5));
    }
    int pass = 0;
    bool _isEnabled = false;
    Timer timer;
    private void SetModuleAccess(object state)
    {
        if (pass > 0)
        {
            if (_isEnabled)
                _isEnabled = false;
            else
                _isEnabled = true;

            foreach (Users.UserModule uModule in ups.model.ups_repository._current_user.UserModules)
            {
                if (uModule.Module_ID == 0)
                {
                    if (_isEnabled == false)
                        uModule.User_Module_Access = 0;
                    else
                        uModule.User_Module_Access = 1;
                }
            }
            if (pass == 2)
                ups.model.ups_repository._current_user.UserModules.Add(new Users.UserModule(8, 1));
        }
        pass++;
    }

    public Users.User CurrentUser
    {
        get { return ups.model.ups_repository._current_user; }
    }

    public ObservableCollection<Module> ModuleCollection
    {
        get { return ModuleKey._module_objects; }
    }
}
public class IsEnabledMultiValueConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        try
        {
            bool userHasAccess = false;
            int ModuleID = (int)values[0];
            bool ModuleEnabled = (bool)values[1];
            string ModuleLicenseDate = (string)values[2];

            Users.User user = values[3] as Users.User;
            Users.UserModule userModule = user.UserModules.SingleOrDefault(um => um.Module_ID == ModuleID);

            DateTimeFormatInfo dtfi = new DateTimeFormatInfo();
            dtfi.ShortDatePattern = "yyyy-MM-dd";
            dtfi.DateSeparator = "-";
            DateTime MLicenseDate = System.Convert.ToDateTime(ModuleLicenseDate, dtfi);

            if (userModule != null)
            {
                userHasAccess = userModule.User_Module_Access > 0;
            }
            return (ModuleEnabled && (MLicenseDate >= DateTime.Now) && userHasAccess);
        }
        catch
        {
            return false;
        }
    }

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

User.cs

public class User : INotifyPropertyChanged
{
    public static User CreateNewUser()
    {
        return new User();
    }

    public User() {}

    public int User_ID { get; set; }
    public string Username { get; set; }
    public string Name { get; set; }
    public string Job_Title { get; set; }
    public string Department { get; set; }
    public string Company { get; set; }
    public string Phone_Office { get; set; }
    public string Phone_Mobile { get; set; }
    public string Email { get; set; }
    public string Password { get; set; }
    public DateTime Last_Login { get; set; }
    public int Status { get; set; }
    public int Session_Timeout { get; set; }

    private ItemsObservableObservableCollection<UserModule> user_modules = new ItemsObservableObservableCollection<UserModule>();

    public ItemsObservableObservableCollection<UserModule> UserModules
    {
        get
        {
            return user_modules;
        }
        set
        {
            user_modules = value;
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void RaisePropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));

    }
}

UserModule.cs

public class UserModule : INotifyPropertyChanged
{
    public UserModule(int Module_ID)
    {
        this.Module_ID = Module_ID;
    }

    public UserModule(int Module_ID, int User_Module_Access)
    {
        this.Module_ID = Module_ID;
        this.User_Module_Access = User_Module_Access;
    }

    private int _module_id;
    private int _userModuleAccess;

    public int Module_ID
    {
        get { return _module_id; }
        set
        {
            _module_id = value;
            RaisePropertyChanged("Module_ID");
        }
    }

    public int User_Module_Access
    {
        get { return _userModuleAccess; }
        set
        {
            _userModuleAccess = value;
            RaisePropertyChanged("User_Module_Access");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void RaisePropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));

    }
}

ups.cs

namespace ups.model
{
    public static class ups_repository
    {
        public static Users.User _current_user = new Users.User();

        public static void LoadUser(string Username, string Key, string Message)
        {
            ...
        }
    }
}

此时按钮正在显示,在上面的VM中,我将集合中的第一个用户模块从启用切换为禁用,每隔五秒钟。我还为集合中的最后一个添加了权限。按钮未启用并禁用它们的方式。

3 个答案:

答案 0 :(得分:1)

您可以在Button的IsEnabled属性上使用MultiValueConverter尝试MultiBinding。 MultiValueConverter将模块绑定到Button;以及当前用户的Module Collection(在下面的示例中,我发送了用户自己)并进行测试以返回bool以指示是否可以启用或禁用Button。

<Window x:Class="WpfHowTo.ItemsControlTestHorn"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:l="clr-namespace:WpfHowTo"
        Title="ItemsControlTestHorn" Height="300" Width="300">
    <Window.Resources>
        <l:MultiValueConverter x:Key="multiValueConverter"/>
    </Window.Resources>
    <Grid Name="gridModules">
        <ItemsControl ItemsSource="{Binding Path=Modules}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <StackPanel>
                        <Button Content="{Binding ModuleAbbreviation}">
                            <Button.IsEnabled>
                                <MultiBinding Converter="{StaticResource multiValueConverter}">
                                    <Binding Path="."/>
                                    <Binding ElementName="gridModules" Path="DataContext.CurrentUser"/>
                                    <!--<Binding RelativeSource="{RelativeSource AncestorType=Grid}" Path="DataContext.CurrentUser"/>-->
                                </MultiBinding>
                            </Button.IsEnabled>
                        </Button>
                    </StackPanel>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl> 
    </Grid>
</Window>

MultiValueConverter就是这样的。

public class MultiValueConverter : IMultiValueConverter
{

    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        bool result = true;

        Module module = values[0] as Module;
        User user = values[1] as User;
        bool userHasAccess = false;

        UserModule userModule = user.UserModules.SingleOrDefault(um => um.Module_ID == module.Module_ID);

        if (userModule != null)
        {
            userHasAccess = userModule.User_Module_Access == 1;
        }

        return result = ! module.ModuleDisabled && module.ModuleLicenseDate > DateTime.Now && userHasAccess;
    }

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

班级定义

public class Module
{
    public int Module_ID { get; set; }
    public string ModuleName { get; set; }
    public string ModuleAbbreviation { get; set; }
    public bool ModuleDisabled { get; set; }
    public DateTime ModuleLicenseDate { get; set; }
}

public class UserModule
{
    public int Module_ID { get; set; }
    public int User_Module_Access { get; set; }
}

public class User
{
    private ObservableCollection<UserModule> _userModules = new ObservableCollection<UserModule>();
    public string UserName { get; set; }

    public ObservableCollection<UserModule> UserModules
    {
        get
        {
            return _userModules;
        }
    }
}

答案 1 :(得分:1)

我想出了一个更简单的想法。但是,我将第一个答案作为MultiBinding的示例。

这是新想法。我使用其他两个集合创建了一个新集合。这项技术现在符合问题的标题。

public IEnumerable<object> Modules
{
    get
    {
        ObservableCollection<Module> modules = GetAllModules();
        User currentUser = GetCurrentUser();

        var accessibleModules = modules.GroupJoin
            (
                currentUser.UserModules, m => m.Module_ID, um => um.Module_ID,
                (m, um) => new
                {
                    ModuleName = m.ModuleName,
                    ModuleAbbreviation = m.ModuleAbbreviation,
                    IsModuleAccessible = !m.ModuleDisabled && m.ModuleLicenseDate > DateTime.Now && (um.Count() == 0 ? -1 : um.Single().User_Module_Access) == 1
                }
            );

        return accessibleModules;
    }
}

在.xaml

<ItemsControl ItemsSource="{Binding Path=Modules}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <StackPanel>
                <Button Content="{Binding ModuleAbbreviation}" IsEnabled="{Binding IsModuleAccessible}" />
            </StackPanel>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

答案 2 :(得分:1)

ItemsObservableObservableCollection

以下类旨在克服ObservableCollection不侦听所包含对象更改的问题。

先决条件是添加到此集合的对象应实现INotifyPropertyChanged接口。

简而言之,这个类注册到项目的PropertyChanged事件并上升ObservableCollection的CollectionChanged事件。

http://msdn.microsoft.com/en-us/magazine/dd252944.aspx

using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Collections;

namespace VJ.Collections
{
    /// <summary>
    ///     This class adds the ability to refresh the list when any property of
    ///     the objects changes in the list which implements the INotifyPropertyChanged. 
    ///
    /// </summary>
    /// <typeparam name="T">
    ///     The type of elements in the collection.
    /// </typeparam>
    public class ItemsObservableObsrvableCollection<T> : ObservableCollection<T> where T : INotifyPropertyChanged
    {
        protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == NotifyCollectionChangedAction.Add)
            {
                RegisterPropertyChanged(e.NewItems);
            }
            else if (e.Action == NotifyCollectionChangedAction.Remove)
            {
                UnRegisterPropertyChanged(e.OldItems);
            }
            else if (e.Action == NotifyCollectionChangedAction.Replace)
            {
                UnRegisterPropertyChanged(e.OldItems);
                RegisterPropertyChanged(e.NewItems);
            }

            base.OnCollectionChanged(e);
        }

        protected override void ClearItems()
        {
            UnRegisterPropertyChanged(this);
            base.ClearItems();
        }

        private void RegisterPropertyChanged(IList items)
        {
            foreach (INotifyPropertyChanged item in items)
            {
                item.PropertyChanged += new PropertyChangedEventHandler(item_PropertyChanged);
            }
        }

        private void UnRegisterPropertyChanged(IList items)
        {
            foreach (INotifyPropertyChanged item in items)
            {
                item.PropertyChanged -= new PropertyChangedEventHandler(item_PropertyChanged);
            }
        }

        private void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        }
    }
}
相关问题