在 Caliburn Micro 中将 xaml UserControl 与视图模型一起使用
Reuse xaml UserControl with viewmodels in Caliburn Micro
在我的 wpf 应用程序中,我使用 Caliburn Micro。现在我创建了一个名为 VariableView.xaml 的 UserControl。 Caliburn Micro 将查找相应的 VariableViewModel.cs。这里没问题。
VariableViewModel class 具有视图使用的三个属性以绑定到:VariableName、VariableValue 和 VariableUnit。例如,对于温度,它们设置为“温度”、“40.5”和“°C”。
在我的 MainViewModel.cs 中,我使用了三次 VariableViewModel。
public class MainViewModel : PropertyChangedBase
{
public VariableViewModel TempatureViewModel{get;}
public VariableViewModel PressureViewModel{get;}
public VariableViewModel HumidityViewModel{get;}
public MainViewModel()
{
TemperatureViewModel = new VariableViewModel("Temperature", "°C", "40.5");
...
}
}
值显示在视图中。
我想做的是从名为 TemperatureVariableViewModel 的 VariableViewModel 派生一个 class,它会在构造函数中自动设置 VariableName、VariableUnits 和 VariableValue,例如:
public class TemperatureVariableViewModel : VariableViewModel
{
public TemperatureVariableViewModel()
: base("Temperature", "°C", "40.5")
{
}
}
将 MainViewModel 中的 VariableViewModel 类型替换为 TemperatureViewModel 类型会产生问题。
现在的问题是view没有显示出来。可能 Caliburn Micro 再也找不到视图对应的视图模型了。我该如何解决?
您可以通过为 ViewLocator.LocateTypeForModelType
使用自定义逻辑来实现此目的,这有助于识别 ViewModel 的视图。
例如,您可以从创建属性 UseViewOf
开始(下面也解释了不带属性的方法)
[AttributeUsage(AttributeTargets.Class,AllowMultiple =false)]
public class UseViewOfAttribute:Attribute
{
public Type SelectedType { get; set; }
public UseViewOfAttribute(Type type) => SelectedType = type;
}
您现在可以使用属性
装饰派生的 class
[UseViewOf(typeof(VariableViewModel))]
public class TemperatureVariableViewModel: VariableViewModel
{
现在终于在您的 Bootstrap
class 中,您可以使用您自己的自定义逻辑覆盖 ViewLocator.LocateTypeForModelType
,如下所示
var existingViewLocator = ViewLocator.LocateTypeForModelType;
ViewLocator.LocateTypeForModelType = (modelType, displayLocation, context) =>
{
var targetType = existingViewLocator(modelType, displayLocation, context);
if(targetType == null && modelType.GetCustomAttributes<UseViewOfAttribute>().Any())
{
var attribute = modelType.GetCustomAttribute<UseViewOfAttribute>();
targetType = existingViewLocator(attribute.SelectedType,displayLocation,context);
}
return targetType;
};
如果您不想使用属性,您可以更改自定义逻辑以检索父视图 Class。例如,
var existingViewLocator = ViewLocator.LocateTypeForModelType;
ViewLocator.LocateTypeForModelType = (modelType, displayLocation, context) =>
{
var targetType = existingViewLocator(modelType, displayLocation, context);
if(targetType == null)
{
while(targetType == null)
{
modelType = modelType.BaseType;
targetType = existingViewLocator(modelType, displayLocation, context);
}
}
return targetType;
};
我更喜欢属性方法,因为它使代码更具可读性(未来的开发人员可以轻松理解哪个视图将用于视图模型)。
在我的 wpf 应用程序中,我使用 Caliburn Micro。现在我创建了一个名为 VariableView.xaml 的 UserControl。 Caliburn Micro 将查找相应的 VariableViewModel.cs。这里没问题。 VariableViewModel class 具有视图使用的三个属性以绑定到:VariableName、VariableValue 和 VariableUnit。例如,对于温度,它们设置为“温度”、“40.5”和“°C”。
在我的 MainViewModel.cs 中,我使用了三次 VariableViewModel。
public class MainViewModel : PropertyChangedBase
{
public VariableViewModel TempatureViewModel{get;}
public VariableViewModel PressureViewModel{get;}
public VariableViewModel HumidityViewModel{get;}
public MainViewModel()
{
TemperatureViewModel = new VariableViewModel("Temperature", "°C", "40.5");
...
}
}
值显示在视图中。 我想做的是从名为 TemperatureVariableViewModel 的 VariableViewModel 派生一个 class,它会在构造函数中自动设置 VariableName、VariableUnits 和 VariableValue,例如:
public class TemperatureVariableViewModel : VariableViewModel
{
public TemperatureVariableViewModel()
: base("Temperature", "°C", "40.5")
{
}
}
将 MainViewModel 中的 VariableViewModel 类型替换为 TemperatureViewModel 类型会产生问题。 现在的问题是view没有显示出来。可能 Caliburn Micro 再也找不到视图对应的视图模型了。我该如何解决?
您可以通过为 ViewLocator.LocateTypeForModelType
使用自定义逻辑来实现此目的,这有助于识别 ViewModel 的视图。
例如,您可以从创建属性 UseViewOf
开始(下面也解释了不带属性的方法)
[AttributeUsage(AttributeTargets.Class,AllowMultiple =false)]
public class UseViewOfAttribute:Attribute
{
public Type SelectedType { get; set; }
public UseViewOfAttribute(Type type) => SelectedType = type;
}
您现在可以使用属性
装饰派生的 class[UseViewOf(typeof(VariableViewModel))]
public class TemperatureVariableViewModel: VariableViewModel
{
现在终于在您的 Bootstrap
class 中,您可以使用您自己的自定义逻辑覆盖 ViewLocator.LocateTypeForModelType
,如下所示
var existingViewLocator = ViewLocator.LocateTypeForModelType;
ViewLocator.LocateTypeForModelType = (modelType, displayLocation, context) =>
{
var targetType = existingViewLocator(modelType, displayLocation, context);
if(targetType == null && modelType.GetCustomAttributes<UseViewOfAttribute>().Any())
{
var attribute = modelType.GetCustomAttribute<UseViewOfAttribute>();
targetType = existingViewLocator(attribute.SelectedType,displayLocation,context);
}
return targetType;
};
如果您不想使用属性,您可以更改自定义逻辑以检索父视图 Class。例如,
var existingViewLocator = ViewLocator.LocateTypeForModelType;
ViewLocator.LocateTypeForModelType = (modelType, displayLocation, context) =>
{
var targetType = existingViewLocator(modelType, displayLocation, context);
if(targetType == null)
{
while(targetType == null)
{
modelType = modelType.BaseType;
targetType = existingViewLocator(modelType, displayLocation, context);
}
}
return targetType;
};
我更喜欢属性方法,因为它使代码更具可读性(未来的开发人员可以轻松理解哪个视图将用于视图模型)。