首页 > 解决方案 > 用户控件基类的可见性/绑定问题

问题描述

我有一个应用程序和一个类库。我正在使用 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>

标签: wpfmvvmdata-bindingvisibilitybase-class

解决方案


嗨,我已经在 github 上上传了示例。请核实。 WPF 示例 1. 从标准 VM 派生三角形 VM 和正弦 VM。因为他们都应该使用一些共同的功能。2. 将 dataContext TriangleView 设置为 TriangleVM 和正弦 VM 相同 3. 调整绑定。


推荐阅读