У меня есть приложение и библиотека классов.Я использую ninject, чтобы связать все viewmodels и Syncfusion как стороннее расширение.
Основное приложение отображает RibbonGallery, содержащую волны синуса и треугольника.При выборе волны свойства волны становятся видимыми для изменения пользователем.
Несколько свойств одинаковы для обеих волн (частота, амплитуда и смещение), поэтому я использую базовый класс usercontrol для реализации компонентов только один раз.
Библиотека классов содержит реализациюимя базового класса usercontrol StandardView.
Каждая волна показывает StandardView и его собственное свойство (Phase).
При выборе волны в главном приложении видимость устанавливается в соответствии с выбором.
При записи ввода в 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>
StandardViewModel:
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>
SineViewModel:
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;
}
}
}
IoC:
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>();
}
}
ViewModelLocator:
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>();
}
Приложение
App.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();
}
}
MainWindow:
<Grid>
<local:CanvasView />
</Grid>
CanvasView:
<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>