根据用户选择的ListBox的当前项的一些条件,启动或禁用按钮用MVVM怎么实现?

nanhe0065 2011-01-06 05:24:49
比如ListBox列出员工列表,当用户选择的员工年龄大于30的时候,禁用某个按钮,否则Enable.
...全文
263 18 打赏 收藏 转发到动态 举报
写回复
用AI写文章
18 条回复
切换为时间正序
请发表友善的回复…
发表回复
iamxiaozhuang 2011-04-01
  • 打赏
  • 举报
回复
我是这么解决的:

private sys_Department currentDeparment;
public sys_Department CurrentDeparment
{
get { return currentDeparment; }
set
{
if (!ReferenceEquals(currentDeparment, value))
{
currentDeparment = value;
this.RaisePropertyChanged(() => this.CurrentDeparment);
((DelegateCommand)DeleteDeptCommand).RaiseCanExecuteChanged();
}
}
}
Sunpire 2011-01-08
  • 打赏
  • 举报
回复
支持楼主的钻研精神,继续努力,共同学习
Sunpire 2011-01-07
  • 打赏
  • 举报
回复
强烈支持楼主! 我还没用过 Prism ,看了楼上的 Command 绑定,我也想找时间试试不使用 Prism 的情况下如何使用,我想应是一样的,受教了。

关于“Employee的Age在1~10岁显示成黄色,10~20岁显示成蓝色,20~30岁绿色,30~40岁橙色”,
我们经常看到在 ViewModel 中重新封装 Model 的一些属性和方法,所以有时真是难以界定一个属性/方法
到底该是 Model 还是 ViewModel 的责职,再加上 Converter 的出现,这就显得更乱了。
像你说的颜色这个,我想Microsoft还是推荐我们使用 Converter 的。

你楼上的这个例子,StudentViewModel 和 Student 是一对一的组合关系,这和你顶楼上给出的例子大不相同,
但紧接着下面的代码给出了 MainPageViewModel ,这时我才明白你在上面给出的类图意思, 确实受教了,thanks.
nanhe0065 2011-01-07
  • 打赏
  • 举报
回复
稍微改一下就已经满足了我这贴子当初的要求。就是加了个<Button Command="{Binding MyCommand}" Content="Go" />!


<Grid x:Name="LayoutRoot" Background="White" DataContext="{Binding Path=AllStudents}">
<StackPanel>
<Button Command="{Binding MyCommand}" Content="Go" />
<ListBox x:Name="listStudents" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}" />
<TextBlock Text="{Binding Age}" />
<!--<Button Command="{Binding MyCommand}" Content="Change Name" />-->
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Grid>
nanhe0065 2011-01-07
  • 打赏
  • 举报
回复
刚刚弄了一个我理想中的比较合理的实现,和我这贴子当初的要求不一样,但ICommand至少用上了:-)


MainPage.xaml
<ListBox x:Name="listStudents" ItemsSource="{Binding Path=AllStudents}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}" />
<TextBlock Text="{Binding Age}" />
<Button Command="{Binding MyCommand}" Content="Change Name" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>




// Model
public class Student
{
public string Name { get; set; }
public int Age { get; set; }
}



// StudentViewModel
public class StudentViewModel : NotificationObject
{
private Student _student;
public StudentViewModel(Student student)
{
this._student = student;
}
public string Name
{
get
{
return _student.Name;
}
set
{
if (_student.Name != value)
{
_student.Name = value;
RaisePropertyChanged(() => this.Name);
}
}
}
public int Age
{
get
{
return _student.Age;
}
set
{
if (_student.Age != value)
{
_student.Age = value;
RaisePropertyChanged(() => this.Age);
}
}
}

private DelegateCommand<object> _cmd;
public ICommand MyCommand
{
get
{
if (_cmd == null)
_cmd = new DelegateCommand<object>(this.Do, this.CanDo);

return _cmd;
}
}
void Do(object param)
{
Name = "new value";
}
bool CanDo(object param)
{
if (Age > 30)
return false;
else
return true;
}
}




// MainPageViewModel
public class MainPageViewModel
{
public ICollectionView AllStudents { get; set; }

public MainPageViewModel()
{
ObservableCollection<StudentViewModel> Students = new ObservableCollection<StudentViewModel>();
for (int i = 0; i < 100; i++)
{
Students.Add(new StudentViewModel(new Student { Name = "Student " + i, Age = 20 + i }));
}
AllStudents = new PagedCollectionView(Students);
}
}


nanhe0065 2011-01-07
  • 打赏
  • 举报
回复
找到解释了,这就是所谓的Master-Detail Binding Scenario.

首先如果想让这种方式起作用,那么一个singleton object(如ContentControl,或者上面例子里的Button)和ItemsControl必须绑定到同一个collection view. 这时singleton object会自动绑定到collection view的CurrentItem. .(如果不是collection view,则不支持这种方式,WPF可以设置ItemsControl的IsSynchronizedWithCurrentItem为true来手动支持这种方式,但silverlight貌似不支持IsSynchronizedWithCurrentItem=true, 设置出抛出异常.)



nanhe0065 2011-01-07
  • 打赏
  • 举报
回复
to sunpire:

如果要求仅仅是我标题说的那种效果的话,我确实一开始没想到把bool CanBeLoved这个属性放在Model后,界面中的Button.IsEnabled再去绑定[列表当前项.CanBeLoved]来轻松解决这个问题。thank you.

CanBeLoved放在ViewModel是基于这么一个考虑。如果获得的Model并不是自己能控制和修改的情况怎么办?或者View需要Model的某个(或某些)字段经过某种计算后的呈现,而这种计算如果放在Model,那么对其他使用这个Model,但不需要这个计算结果的类来讲会比较困惑等等诸如此类情况。

就比如Employee的Age在1~10岁显示成黄色,10~20岁显示成蓝色,20~30岁绿色,30~40岁橙色,Model可以自己控制时这个你当然可以选择放在Model里。但这种颜色属性貌似不是员工Model应具有的固有属性。这种时候就可以在ViewModel里作文章,结合Conveter之类的。

问题还是回到ICommand,我认为我的问题本质上是ICommand的问题。




nanhe0065 2011-01-07
  • 打赏
  • 举报
回复
to sunpire:

解决这个问题的过程中受你启发很大,thank you, again.

假如说这个问题从另一个角度是关注ICommand的话,刚刚我又发现一个问题:

<!--
如果DataGrid的DataContext绑定的是ObservableCollection,那么MyCommand的CanExecute逻辑不起作用。
换作绑定ICollectionView则没问题,我估计绑定CollectionViewSource也是可以的。
我觉得这里肯定有个说道,而且应该属于非常基础级的知识点。
说真的,别看是小问题,我感觉暴露出我很多知识盲点。
-->
<Grid x:Name="LayoutRoot" Background="White" DataContext="{Binding Path=AllStudents}">
<StackPanel>
<Button Command="{Binding MyCommand}" Content="Go" />
<ListBox x:Name="listStudents" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}" />
<TextBlock Text="{Binding Age}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Grid>


Sunpire 2011-01-07
  • 打赏
  • 举报
回复
to 楼主:
“就算Employee Model本身没有这个CanBeLoved属性,也可以在Employee的ViewMoel里增加这个属性,给View一个Model有这么个固有属性的假像。”
在 ViewMoel里增加这个属性 的话要写的代码会更多,因为你得要去触发
this.RaisePropertyChanged("CanBeLoved"),否则你的UI不会有变化,就像你1楼的 BtnEnabled 属性一样。

但是在 Model 中增加这个属性却只需要实现 getter 就可以, setter 都不用着实现 ---- 这是我今天中午看了你的 BtnEnabled 属性后想到的,你不妨帮我验证一下我的推断是不是对的。如果我的推断是对的,那么相信你能想得到原因的。
Sunpire 2011-01-06
  • 打赏
  • 举报
回复
Age <=30 这个可以作为 Model 中的一个属性,如

public class Employee {
public bool CanBeLoved{
get{ return Age <=30; } // 老兄你也太狠了,0-30岁的通杀,30以上的不要
}
}


<Button Command="{Binding Path=MyCommand}" IsEnabled="{Binding Path=Employees.CurrentItem.CanBeLoved}" Content="I love you." />

不知这样当 Employees.CurrentItem == null 时会如何。

或者试试

<Button Command="{Binding Path=MyCommand}"
IsEnabled="{Binding SelectedItem.CanBeLoved,ElementName=listBox1}"
Content="I love you." />

Sunpire 2011-01-06
  • 打赏
  • 举报
回复
to 楼主:
呵呵,我从 Employee 的责职上看, 像 CanBeLoved 这样的属性是可以作为 Employee 自身的责职的,
你给出的类图我再体会体会,这两天面试把我给紧张得~~
nanhe0065 2011-01-06
  • 打赏
  • 举报
回复
误打误撞弄出来了。不用Button的IsEnabled绑定ViewModel中的某个属性,在ICommand.CanExecute判断Button是否应该Enabled(其实这样像个Command不是吗?),但问题是我搞不懂为什么要从CommandParameter传当前项才会运作。

<Button Command="{Binding Path=MyCommand}" CommandParameter="{Binding Path=Employees.CurrentItem}" Content="I love you."  />



// ViewModel
public class EmployeeViewModel : NotificationObject
{
public ICollectionView Employees { get; private set; }

public ICommand MyCommand { get; private set; }

public Employee CurrentEmployee
{
get
{
return current;
}
set
{
if (value != current)
{
current = value;
RaisePropertyChanged("CurrentEmployee");
}
}
}

private readonly ObservableCollection<Employee> employees;

private Employee current;

public EmployeeViewModel()
{
employees = new ObservableCollection<Employee>();

for (int i = 0; i < 100; i++)
employees.Add(new Employee { Name = "Employee" + i, Age = 20 + i });

Employees = new PagedCollectionView(employees);


Employees.CurrentChanged += (s, e) =>
{
CurrentEmployee = Employees.CurrentItem as Employee;
};
CurrentEmployee = Employees.CurrentItem as Employee;

MyCommand = new DelegateCommand<object>(this.OnSubmit, this.CanSubmit);
}



private void OnSubmit(object param)
{
CurrentEmployee.Name = "change name";
}

public bool CanSubmit(object param)
{
return CurrentEmployee.Age <=30 ? true : false;
}

}

nanhe0065 2011-01-06
  • 打赏
  • 举报
回复
to:sunpire

你说的也有道理。就算Employee Model本身没有这个CanBeLoved属性,也可以在Employee的ViewMoel(对应下图Josh Smith的CustomerViewModel)里增加这个属性,给View一个Model有这么个固有属性的假像。



按Josh Smith的这种结构组织类也是值得借鉴的。不过还是那个问题,没有合理利用到ICommand.CanExecute,我还是觉得少了些什么。。。
passself 2011-01-06
  • 打赏
  • 举报
回复
没有啥简单的,只能循环遍历,来设置属性enable:disable
nanhe0065 2011-01-06
  • 打赏
  • 举报
回复

// 继承自Prism 4的NotificationObject
public class EmployeeViewModel : NotificationObject
{
private bool btnEnabled;
public bool BtnEnabled //与界面中Button的IsEnabled属性绑定
{
get
{
return this.btnEnabled;
}
set
{
if (value != btnEnabled)
{
this.btnEnabled = value;
this.RaisePropertyChanged(()=>this.BtnEnabled);
}
}
}

public ICollectionView Employees { get; private set; }

public EmployeeViewModel()
{
Employees = new PagedCollectionView(employees);

Employees.CurrentChanged += (s, e) =>
{
var CurrentEmployee = Employees.CurrentItem as Employee;
BtnEnabled = CurrentEmployee.Age <= 30 ? true : false;

};
}
}


<Button Command="{Binding MyCommand}" Content="I love you." IsEnabled="{Binding BtnEnabled}"  />


我的问题是有没有更简单的办法? 本来想用RelayCommand或DelegateCommand的CanExecute,但用户选择列表项时,它不去检查Button的CanExecute,也就无法启用禁用按钮。

有没有办法不使用按钮IsEnabled绑定,仅仅用DelegateCommand的CanExecute来根据条件改变Button的IsEnabled状态?
nanhe0065 2011-01-06
  • 打赏
  • 举报
回复
不把WPF/Silverlight的Commanding的设计初衷当回事了。
nanhe0065 2011-01-06
  • 打赏
  • 举报
回复
谢谢sunpire回复,问题是Button的IsEnabled另外绑定ViewModel的一个属性来控制它的可访问性这种方式,在我看来像是退化了,根据不把WPF/Silverlight的Commanding的设计初衷当回事了。

ICommand的CanExecute不是用来当摆设的,right?

8,735

社区成员

发帖
与我相关
我的任务
社区描述
WPF/Silverlight相关讨论
社区管理员
  • WPF/Silverlight社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧