我有两个组合框:类别和类型。最初显示我的表单时,我列出了数据库中存在的所有类别和类型。对于这两个组合框,我手动插入第0行,使其值为“全部”,这样他们就不必选择任何一个,如果他们不想这样做。
我将两个组合框绑定到ReactiveObjects,以便在用户选择类别时,类型组合框会自动重新填充查询,以仅显示与所选类别相关的类型以及添加的行0。
当用户选择一个类别时,它会正确运行查询,返回相关类型,正确添加行0并正确填充组合框;但是,在XAML大小上,它不是选择第0行,而是在组合框周围添加红色轮廓,表示进行了无效选择。
如果没有选择Type组合框并提交表单,则传递正确的值0。因此,当一切正常工作时,类型组合框周围的红框正在向用户传达他们做错了什么,我无法确定为什么XAML没有拿起所选的值。我运行代码时没有添加第0行,它仍然具有相同的行为,即组合框已正确填充,但没有选择行并显示红色轮廓。
XAML for comboboxes
<ComboBox
Grid.Row="3"
Grid.Column="1"
Width="200"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Style="{StaticResource SimpleComboBox}"
ItemsSource="{Binding Categories}"
SelectedValue="{Binding SearchCriteria.CategoryID}"
SelectedValuePath="ComboValueID"
DisplayMemberPath="ComboDataValue"
/>
<TextBlock
Grid.Row="3"
Grid.Column="2"
Style="{StaticResource NormalTextNarrow}"
Text="Type" VerticalAlignment="Top"
/>
<ComboBox
Grid.Row="3"
Grid.Column="3"
Width="200"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Style="{StaticResource SimpleComboBox}"
ItemsSource="{Binding Types}"
SelectedValue="{Binding SearchCriteria.TypeId}"
SelectedValuePath="ComboValueID"
DisplayMemberPath="ComboDataValue"
/>
相关的虚拟机代码
// Definition of SearchCriteria. ResourceItem is a ReactiveObject and
// all of the relevant properties watch for changes in values.
private ResourceItem searchCriteria;
public ResourceItem SearchCriteria
{
get { return searchCriteria; }
set { this.RaiseAndSetIfChanged(ref searchCriteria, value); }
}
// This all happens in my constructor
// Defining Row 0
var b = new GenericCombobox { ComboValueID = 0, ComboDataValue = "All" };
// Populating the comboboxes from the database
Categories = omr.GetKTValues("RES_CATEGORIES");
Types = omr.GetKTValuesRU("RES_TYPES");
// Adding the row 0
Categories.Insert(0, b);
Types.Insert(0, b);
// The form is displayed correctly at this point with the row 0 selected
问题代码
// When the user picks a category, this is the method that is invoked:
private void categoryChanged()
{
if (SearchCriteria.CategoryID != 0)
{
Types = rr.GetCategoryTypes(SearchCriteria.CategoryID);
SearchCriteria.TypeId = 0;
}
}
// This runs correctly and returns the relevant Types
public List<GenericCombobox> GetCategoryTypes(int categoryId)
{
string sql = "res.usp_GetCategoryTypes";
var types = new List<GenericCombobox>();
SqlConnection sqlCn = DatabaseCommunication.OpenConnection();
using (SqlCommand cmd = new SqlCommand(sql, sqlCn))
{
// Omitting db stuff for brevity...
try
{
SqlDataReader dr = cmd.ExecuteReader();
while (dr.Read())
{
types.Add(new GenericCombobox
{
ComboValueID = (int)dr["TypeId"],
ComboDataValue = (string)dr["Type"],
IsSelected = false,
Description = (string)dr["Type"],
ComboDataCode = (string)dr["Type"]
});
}
// More db-stuff omitted
}
// Adding the row 0
var b = new GenericCombobox { ComboValueID = 0, ComboDataValue = "All", IsSelected = false, Description = "All", ComboDataCode = "All" };
types.Insert(0, b);
return types;
}
使用附加代码进行更新
// Object containing the TypeId property
public class ResourceItem : ReactiveObject, ISelectable
{
public int Id { get; set; }
public int? OriginalItemId { get; set; }
// ...many other properties...
private int typeId;
public int TypeId
{
get { return typeId; }
set { this.RaiseAndSetIfChanged(ref typeId, value); }
}
// ...and many more follow...
答案 0 :(得分:1)
我已经能够重现这个问题了,我发现了一些可以让它停止发生的愚蠢的事情。
如果我在“全部”以外的类型中选择一个项目,那么当我在类别中更改选择时,它会在类型中选择“全部”。
如果在categoryChanged()
中我替换此行...
SearchCriteria.TypeId = 0;
这一个:
SearchCriteria = new ResourceItem() {
TypeId = 0,
CategoryID = SearchCriteria.CategoryID
};
如果我ResourceItem.TypeId.set
PropertyChanged
加注SelectedItem
,无论该值是否已更改,它都会再次正常运行。
我的假设是,当集合发生变化时,类型组合框中的SelectedValue
(你甚至不使用!)并没有改变,因为你没有告诉它更新SearchCriteria.TypeId = 0
。
当SearchCriteria.TypeId
已经等于零时设置RaiseAndSetIfChanged()
是无操作,因为PropertyChanged
正如名称所示:它检查值是否真的发生了变化,并且如果没有,则不会引发SelectedValue
。
SelectedItem
偶然发生与新“全部”项目相同的值,但组合框不关心。它只知道没有人告诉它去寻找一个新的ItemsSource
,它所拥有的旧版本已经不再好了,因为它不在private void categoryChanged()
{
if (SearchCriteria.CategoryID != 0)
{
Types = rr.GetCategoryTypes(SearchCriteria.CategoryID);
SearchCriteria.SelectedType = Types.FirstOrDefault();
//SearchCriteria.TypeId = 0;
//SearchCriteria = new ResourceItem() { TypeId = 0, CategoryID = SearchCriteria.CategoryID };
}
}
中。
所以这也有效:
<ComboBox
Grid.Row="3"
Grid.Column="3"
Width="200"
HorizontalAlignment="Left"
VerticalAlignment="Top"
ItemsSource="{Binding Types}"
SelectedValue="{Binding SearchCriteria.TypeId}"
SelectedItem="{Binding SearchCriteria.SelectedType}"
SelectedValuePath="ComboValueID"
DisplayMemberPath="ComboDataValue"
/>
XAML:
private GenericCombobox selectedType;
public GenericCombobox SelectedType
{
get { return selectedType; }
set { this.RaiseAndSetIfChanged(ref selectedType, value); }
}
类ResourceItem
private void categoryChanged()
{
if (SearchCriteria.CategoryID != 0)
{
Types = rr.GetCategoryTypes(SearchCriteria.CategoryID);
SearchCriteria = new ResourceItem() {
TypeId = 0,
CategoryID = SearchCriteria.CategoryID
};
// Do you need to do this?
// SearchCriteria.PropertyChanged += SearchCriteria_PropertyChanged;
}
}
我认为最好的选择是上面的选项#2:
categoryChanged()
这里的潜在问题是,在我的测试代码中,我从PropertyChanged
上的SearchCriteria
处理程序调用了SearchCriteria
。如果我创建一个新的SelectedItem
,我需要确保我在新的事件上处理该事件。
考虑到这一点,也许在类型组合框上绑定public class DataPoint: IComparable<DataPoint>
{
public List<decimal> points;
[...]
public int CompareTo(DataPoint that)
{
if (this.points.Average() > that.points.Average()) return -1;
if (this.points.Average() == that.points.Average()) return 0;
return 1;
}
}
毕竟是最好的解决方案:这是我能想到的唯一一个不需要视图模型做奇怪事情来弥补不良行为的问题。认为它真的不应该知道。