wpf - 用户控件基类的可见性/绑定问题
问题描述
我有一个应用程序和一个类库。我正在使用 ninject 将所有视图模型和 Syncfusion 绑定为第三方扩展。
主应用程序显示包含正弦波和三角波的 RibbonGallery。选择波形时,波形属性变为可见,用户可以更改。
两个波的几个属性(频率、振幅和偏移)是相同的,所以我使用用户控件基类来实现组件一次。
类库包含用户控件基类名称 StandardView 的实现。
每个 wave 都显示了他自己的 StandardView 和属性(阶段)。
在主应用程序中选择波浪时,会根据选择设置可见性。
在 TextBox 中写入输入时,ComboBox 会自动弹出,用户可以从中进行选择。
我的问题是,如果用户将输入写入正弦波,然后他将选择更改为三角波,然后他又回到正弦波 - 他将无法从 ComboBox 中选择任何项目 - 就好像这些项目被冻结了一样。
我怀疑 ComboBox 弹出正确,然后是前面的 ComboBox 弹出,这导致用户无法选择任何项目。
任何帮助将不胜感激。
编辑:(在问题中添加了源代码)
类库: StandardView(仅适用于频率)
<StackPanel Orientation="Horizontal">
<!--Standard Frequency-->
<Label Content="Frequency" Width="120" Margin="2" VerticalAlignment="Center"></Label>
<syncfusion:DoubleTextBox Name="StandardFrequency" Width="140" Margin="2" Padding="1"
TextWrapping="NoWrap" Focusable="True"
EnterToMoveNext="True" AcceptsReturn="False"
IsReadOnly="{Binding ElementName=readonly, Path=IsChecked}"
Value="{Binding FrequencyValue, Source={x:Static local:ViewModelLocator.StandardViewModel}, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
NumberDecimalDigits="8"
HorizontalAlignment="Center" VerticalAlignment="Center" TextAlignment="Center"
VerticalContentAlignment="Center" HorizontalContentAlignment="Center"
ContextMenu="{x:Null}" >
<syncfusion:DoubleTextBox.InputBindings>
<KeyBinding Command="{Binding Path=ApplyValue}" Key="Enter" />
</syncfusion:DoubleTextBox.InputBindings>
</syncfusion:DoubleTextBox>
<ComboBox Name="StandardFrequencyVariable" Width="70" Margin="2" Padding="1"
ItemsSource="{Binding FrequencyValues, Source={x:Static local:ViewModelLocator.StandardViewModel}}" DisplayMemberPath="Key" SelectedValuePath="Key"
SelectedValue="{Binding FrequencyNodeCategory, Source={x:Static local:ViewModelLocator.StandardViewModel}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsDropDownOpen="{Binding IsFrequencyDropDownOpen, Source={x:Static local:ViewModelLocator.StandardViewModel}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsHitTestVisible="False" >
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem" >
<Setter Property="Focusable" Value="False"/>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
</StackPanel>
标准视图模型:
public class StandardViewModel : INotifyPropertyChanged
{
public StandardViewModel()
{
}
#region Public Properties
private double frequencyValue;
public double FrequencyValue
{
get
{
return frequencyValue;
}
set
{
if( value != frequencyValue )
{
frequencyValue = value;
OnPropertyRaised( "FrequencyValue" );
IsFrequencyDropDownOpen = true;
}
}
}
private string frequencyNodeCategory;
public string FrequencyNodeCategory
{
get
{
return frequencyNodeCategory;
}
set
{
if( value != frequencyNodeCategory )
{
frequencyNodeCategory = value;
OnPropertyRaised( "FrequencyNodeCategory" );
IsFrequencyDropDownOpen = false;
}
}
}
private bool isFrequencyDropDownOpen;
public bool IsFrequencyDropDownOpen
{
get
{
return isFrequencyDropDownOpen;
}
set
{
if( value != isFrequencyDropDownOpen )
{
isFrequencyDropDownOpen = value;
OnPropertyRaised( "IsFrequencyDropDownOpen" );
if( isFrequencyDropDownOpen )
return;
}
}
}
public Dictionary<string, int> FrequencyValues
{
get
{
return frequencyValues;
}
set
{
frequencyValues = value;
}
}
public Dictionary<string, int> frequencyValues = new Dictionary<string, int>(){
{"µHz", -6},
{"mHz", -3},
{"Hz", 0},
{"KHz", 3},
{"MHz", 6},
{"GHz", 9}
};
#endregion
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyRaised( string propertyName = null )
{
PropertyChangedEventHandler handler = PropertyChanged;
if( handler != null )
handler( this, new PropertyChangedEventArgs( propertyName ) );
}
}
SineView:(TriangleView基本一样)
<StackPanel Orientation="Vertical">
<local:StandardView />
<StackPanel Orientation="Horizontal">
<!--Phase-->
<Label Content="Phase" Width="120" Margin="2" VerticalAlignment="Center" ></Label>
<syncfusion:DoubleTextBox Name="StandardSinePhase" Width="140" Margin="2" Padding="1"
TextWrapping="NoWrap" Focusable="True"
EnterToMoveNext="True" AcceptsReturn="False"
IsReadOnly="{Binding ElementName=readonly, Path=IsChecked}"
Value="{Binding StandardSinePhaseValue, Source={x:Static local:ViewModelLocator.SineViewModel}, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
NumberDecimalDigits="1"
HorizontalAlignment="Center" VerticalAlignment="Center" TextAlignment="Center"
VerticalContentAlignment="Center" HorizontalContentAlignment="Center"
ContextMenu="{x:Null}" >
<syncfusion:DoubleTextBox.InputBindings>
<KeyBinding Command="{Binding Path=ApplyValue}" Key="Enter" />
</syncfusion:DoubleTextBox.InputBindings>
</syncfusion:DoubleTextBox>
<Label Name="PhaseDegLabel" Content="Deg." Margin="2" Height="30" VerticalAlignment="Center" HorizontalAlignment="Left" HorizontalContentAlignment="Left" VerticalContentAlignment="Center" ></Label>
</StackPanel>
</StackPanel>
正弦视图模型:
public class SineViewModel : INotifyPropertyChanged
{
public SineViewModel()
{
}
private double standardSinePhaseValue;
public double StandardSinePhaseValue
{
get
{
return standardSinePhaseValue;
}
set
{
if( value != standardSinePhaseValue )
{
standardSinePhaseValue = value;
OnPropertyRaised( "StandardSinePhaseValue" );
}
}
}
private bool isSineChecked = true;
public bool IsSineChecked
{
get
{
return isSineChecked;
}
set
{
if( value != isSineChecked )
{
isSineChecked = value;
OnPropertyRaised( "IsSineChecked" );
if( isSineChecked == true )
{
}
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyRaised( string propertyName = null )
{
PropertyChangedEventHandler handler = PropertyChanged;
if( handler != null )
handler( this, new PropertyChangedEventArgs( propertyName ) );
}
}
BooleanToVisibilityConverter:
public class BooleanToVisibilityConverter : IValueConverter
{
private bool triggerValue = false;
public bool TriggerValue
{
get
{
return triggerValue;
}
set
{
triggerValue = value;
}
}
private bool isHidden;
public bool IsHidden
{
get
{
return isHidden;
}
set
{
isHidden = value;
}
}
private object GetVisibility( object value )
{
if( !( value is bool ) )
return DependencyProperty.UnsetValue;
bool objValue = ( bool )value;
if( ( objValue && TriggerValue && IsHidden ) || ( !objValue && !TriggerValue && IsHidden ) )
{
return Visibility.Hidden;
}
if( ( objValue && TriggerValue && !IsHidden ) || ( !objValue && !TriggerValue && !IsHidden ) )
{
return Visibility.Collapsed;
}
return Visibility.Visible;
}
public object Convert( object value, Type targetType, object parameter, CultureInfo culture )
{
return GetVisibility( value );
}
public object ConvertBack( object value, Type targetType, object parameter, CultureInfo culture )
{
if( ( Visibility )value == Visibility.Visible )
{
return true;
}
else
{
return false;
}
}
}
国际奥委会:
public static class IoC
{
#region Public Properties
/// <summary>
/// The kernel for the IoC container
/// </summary>
public static IKernel Kernel { get; private set; } = new StandardKernel();
#endregion
#region Construction
/// <summary>
/// Setups the IoC container, binds all information required
/// </summary>
public static void Setup()
{
// Bind all required view models
BindViewModels();
}
/// <summary>
/// Binds all singleton view models.
/// </summary>
private static void BindViewModels()
{
// Bind to a single instance of application view model
Kernel.Bind<CanvasViewModel>().ToConstant( new CanvasViewModel() );
Kernel.Bind<SineViewModel>().ToConstant( new SineViewModel() );
Kernel.Bind<TriangleViewModel>().ToConstant( new TriangleViewModel() );
Kernel.Bind<StandardViewModel>().ToConstant( new StandardViewModel() );
}
#endregion
/// <summary>
/// Gets a service from the IoC, of the specified type
/// </summary>
/// <typeparam name="T"> the type to get</typeparam>
/// <returns></returns>
internal static T Get<T>()
{
return Kernel.Get<T>();
}
}
视图模型定位器:
public class ViewModelLocator
{
public static ViewModelLocator Instance { get; private set; } = new ViewModelLocator();
public static CanvasViewModel CanvasViewModel => IoC.Get<CanvasViewModel>();
public static SineViewModel SineViewModel => IoC.Get<SineViewModel>();
public static TriangleViewModel TriangleViewModel => IoC.Get<TriangleViewModel>();
public static StandardViewModel StandardViewModel => IoC.Get<StandardViewModel>();
}
应用
应用程序.xaml.cs:
public partial class App : Application
{
protected override void OnStartup( StartupEventArgs e )
{
// Setup IoC
IoC.Setup();
// Show the main window
Current.MainWindow = new MainWindow();
Current.MainWindow.Show();
}
}
主窗口:
<Grid>
<local:CanvasView />
</Grid>
画布视图:
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.Resources>
<!--Hides control if boolean value is true-->
<core:BooleanToVisibilityConverter x:Key="HiddenIfTrue" TriggerValue="True" IsHidden="True"/>
<!--Hides control if boolean value is false-->
<core:BooleanToVisibilityConverter x:Key="HiddenIfFalse" TriggerValue="False" IsHidden="True"/>
<!--Collapses control if boolean value is true InvBoolToVis-->
<core:BooleanToVisibilityConverter x:Key="CollapsedIfTrue" TriggerValue="True" IsHidden="False"/>
<!--Collapses control if boolean value is false BoolToVis-->
<core:BooleanToVisibilityConverter x:Key="CollapsedIfFalse" TriggerValue="False" IsHidden="False"/>
</Grid.Resources>
<syncfusion:RibbonBar Grid.Row="0" Header="Waves" IsLauncherButtonVisible="False" >
<syncfusion:RibbonGallery Name="Standard" ItemWidth="90" ExpandWidth="0" MenuIconBarEnabled="True" VisualMode="InRibbon" >
<syncfusion:RibbonGalleryItem Name="Sine" Content="Sine"
Command="{Binding Path=SineCommand, Source={x:Static core:ViewModelLocator.CanvasViewModel}}"
IsChecked="{Binding Path=IsSineChecked, Source={x:Static core:ViewModelLocator.SineViewModel}, UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}" />
<syncfusion:RibbonGalleryItem Name="Triangle" Content="Triangle"
Command="{Binding Path=TriangleCommand, Source={x:Static core:ViewModelLocator.CanvasViewModel}}"
IsChecked="{Binding Path=IsTriangleChecked, Source={x:Static core:ViewModelLocator.TriangleViewModel}, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
</syncfusion:RibbonGallery>
</syncfusion:RibbonBar>
<StackPanel Grid.Row="1" Margin="0,0,10,0" >
<syncfusion:GroupBar Name="ParametersPanel" AllowDragandDrop="False" HorizontalAlignment="Left" Margin="2" MinWidth="350" ItemHeaderHeight="30" VisualMode="MultipleExpansion" AnimationSpeed="0" >
<!--Group Bar Item-->
<syncfusion:GroupBarItem x:Name="StandardParameters" HeaderText="{Binding StandardParametersCaption, Source={x:Static core:ViewModelLocator.CanvasViewModel}}"
ShowInGroupBar="True" IsExpanded="True" >
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<core:SineView Grid.Row="0" Visibility="{Binding Path=IsSineChecked,
Source={x:Static core:ViewModelLocator.SineViewModel},
Converter={StaticResource CollapsedIfFalse},UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}" />
<core:TriangleView Grid.Row="0" Visibility="{Binding Path=IsTriangleChecked,
Source={x:Static core:ViewModelLocator.TriangleViewModel},
Converter={StaticResource CollapsedIfFalse},UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}" />
</Grid>
</syncfusion:GroupBarItem>
</syncfusion:GroupBar>
</StackPanel>
</Grid>
解决方案
嗨,我已经在 github 上上传了示例。请核实。 WPF 示例 1. 从标准 VM 派生三角形 VM 和正弦 VM。因为他们都应该使用一些共同的功能。2. 将 dataContext TriangleView 设置为 TriangleVM 和正弦 VM 相同 3. 调整绑定。
推荐阅读
- javascript - 在 React 打字稿中,如何将复选框状态从 Sidebar 组件传递到 MainContent 组件以刷新页面
- memory - GDB 是否支持演绎内存扫描,例如 Cheat Engine?
- python - 比较2个字符串的问题,应该相等
- docker - 将 Drupal 本地环境迁移到 Docker/Container
- java - JavaFX Cell 和 setCellFactory 混淆
- typescript - 如何理解打字稿中的 NonNullable?
- ansible - 带有 if else 语句的 ansible dest
- javascript - webkit fullscreenchange 没有触发,webkitendfullscreen 实际上不是在全屏结束时 - IOS
- linux - NASM 中最简单的 printf 工作示例失败
- yocto - 我可以在 Yocto 构建中包含 OpenWRT 的统一配置接口 (UCI) 吗?