用户控件基类的可见性/绑定问题

时间:2018-12-23 08:33:55

标签: wpf mvvm data-binding visibility base-class

我有一个应用程序和一个类库。 我正在使用ninject绑定所有视图模型和Syncfusion作为第三方扩展。

主应用程序显示一个包含正弦波和三角波的RibbonGallery。 选择波浪时,用户可以看到波浪属性的变化。

两个波的几个属性是相同的(频率,振幅和偏移),所以我使用一个usercontrol基类来仅一次实现这些分量。

类库包含用户控件基类名称的实现 StandardView。

每个wave都显示StandardView及其自己的属性(阶段)。

在主应用中选择波浪时,可见性将根据选择进行设置。

在TextBox中写入输入时,ComboBox会自动弹出,用户可以从中选择。

我的问题是,如果用户将输入写入正弦波,则他将选择更改为三角波,然后再次返回正弦波-他将无法从ComboBox中选择任何项-就像这些项被冻结。

我怀疑ComboBox弹出窗口正确,然后是前面的ComboBox弹出窗口,这导致用户无法选择任何项目。

Video example of the problem

Source code

任何帮助将不胜感激。

编辑:(向问题中添加了源代码)

课程库: 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>

1 个答案:

答案 0 :(得分:0)

嗨,我在github上上传了示例。请验证。 WPF Sample  1.从标准VM派生三角形VM和正弦VM。因为它们都应该使用一些通用功能。  2.将dataContext TriangleView设置为TriangleVM,对于正弦VM设置相同  3.调整绑定。