好的,
我看过几个类似的问题,但过去几天都无法弄清楚这个问题。我有两个组合框,我希望每个组合框在另一个组合框中隐藏所选元素。例如,如果我在ComboBox 1中选择一个值,那么选择项应作为ComboBox 2中的选项删除。
我考虑过使用命令,但ComboBoxes没有命令。我已经粘贴在组合框的XAML和ViewModel代码下面。我很感激任何帮助。我知道下面的代码是错误的,但我认为这个逻辑应该在有限的ItemSource的setter中。
<ComboBox Margin="0,7,0,0"
Name="ComboBoxA"
HorizontalAlignment="Stretch"
Header="{Binding AccountHeader}"
ItemTemplate="{StaticResource ComboBoxTemplate}"
ItemsSource="{Binding ChargedAccounts,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding SelectedAccount,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}" />
<ComboBox x:Uid="TargetAccountTextBox"
Name="ComboBoxB"
Margin="0,7,0,0"
HorizontalAlignment="Stretch"
Header="target account"
ItemTemplate="{StaticResource ComboBoxTemplate}"
ItemsSource="{Binding TargetAccounts,
Mode=TwoWay,
namespace MoneyFox.Shared.ViewModels
{
[ImplementPropertyChanged]
public class ModifyPaymentViewModel : BaseViewModel, IDisposable
{
private readonly IDefaultManager defaultManager;
private readonly IDialogService dialogService;
private readonly IPaymentManager paymentManager;
//this token ensures that we will be notified when a message is sent.
private readonly MvxSubscriptionToken token;
private readonly IUnitOfWork unitOfWork;
// This has to be static in order to keep the value even if you leave the page to select a category.
private double amount;
private Payment selectedPayment;
public ModifyPaymentViewModel(IUnitOfWork unitOfWork,
IDialogService dialogService,
IPaymentManager paymentManager,
IDefaultManager defaultManager)
{
this.unitOfWork = unitOfWork;
this.dialogService = dialogService;
this.paymentManager = paymentManager;
this.defaultManager = defaultManager;
TargetAccounts = unitOfWork.AccountRepository.Data;
ChargedAccounts = unitOfWork.AccountRepository.Data;
token = MessageHub.Subscribe<CategorySelectedMessage>(ReceiveMessage);
}
ObservableCollection<Account> _SelectedAccount;
ObservableCollection<Account> SelectedAccount
{
get
{
return _SelectedAccount;
}
set
{
_SelectedAccount = value;
for(int i = 0; i < ChargedAccounts.Count; i++)
{
if(ChargedAccounts[i].ToString() == _SelectedAccount.ToString())
{
ChargedAccounts.Remove(ChargedAccounts[i]);
}
}
}
}
ObservableCollection<Account> _TargetAccount;
ObservableCollection<Account> Targetccount
{
get
{
return _SelectedAccount;
}
set
{
_SelectedAccount = value;
for (int i = 0; i < TargetAccounts.Count; i++)
{
if (TargetAccounts[i].ToString() == _SelectedAccount.ToString())
{
TargetAccounts.Remove(ChargedAccounts[i]);
}
}
}
}
答案 0 :(得分:0)
虽然我同意Ed提供的答案中的许多要点,但有一种更简单的方法可以在没有DataTriggers或转换器的情况下执行此操作。框架中已经有一个可过滤的CollectionViewSource是你的朋友(Scott Hanselman loves it)
我会将ComboBoxA绑定到您的常规ChargedAccounts
属性,但我会将ComboBoxB修改为:
粗略地说,这可以在几行中完成:
public ICollectionView FilteredData { get; set; }
private void ComboBoxA_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
var z = new CollectionViewSource {Source = ViewModel.ChargedAccounts.Where(p => p != ViewModel.SelectedAccount) };
FilteredData = z.View;
}
当然,假设您在视图后面的代码中具有ViewModel属性,最好将其作为接口公开,并且ChargedAccounts
和SelectedAccount
属性为可通过该界面获得
您还可以在viewmodel中将这几行拼凑在一起并通过SelectedAccount
上的属性更改触发它 - 我只是认为响应UI操作的过滤操作应该放在UI后面的代码中,但那个决定真的取决于你。
答案 1 :(得分:-1)
为组合框提供一个带有数据触发器的ItemContainerStyle(TargetType="ComboBoxItem"
)。对于ComboBoxA
,这看起来像这样:
<ComboBox
...
x:Name="ComboBoxA"
...
>
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem">
<Style.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding
Converter="{local:ObjectEquals}"
>
<Binding
Path="SelectedItem"
ElementName="ComboBoxB" />
<!-- Binding with no properties just binds to the DataContext -->
<Binding />
</MultiBinding>
</DataTrigger.Binding>
<Setter
Property="Visibility"
Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
ComboBoxB
获得相同的优惠,但ElementName="ComboBoxA"
约束中的SelectedItem
。
我们需要编写多值转换器。这很简单:
public class ObjectEquals : MarkupExtension, IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return values.Length == 2 && values[0] == values[1];
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
}
如果可以将DataTrigger.Value
绑定到{Binding}
,那将非常方便,但它不是依赖属性。
你*也可以在viewmodel中暂时删除SelectedAccount
中的TargetAccounts
- 你有一个私有的完整_targetAccountsFull
列表,以及一个公开过滤的列表。 SelectedAccount
的setter会过滤列表。你准备这样做了吗?
但这不是我想要的好解决方案。隐藏组合框项目是UI设计的东西; viewmodel不应该参与其中,事实上甚至不应该意识到这样的事情发生了。 WPF / MVVM的一个乐趣是你可以将这些东西分离成视图中的纯UI代码。 viewmodel有其自身的复杂性需要担心。
顺便说一下,您将SelectedItem
绑定到SelectedAccount
,但SelectedAccount
是ObservableCollection
。这是没有意义的。有一个选定的帐户。使它成为一个Account
,而不是它们的集合。