【问题标题】:Bind ColumnSpan in XAML in Universal App Windows 10在通用应用程序 Windows 10 中的 XAML 中绑定 ColumnSpan
【发布时间】:2015-12-28 10:54:07
【问题描述】:

我正在尝试根据我的 ViewModel 在 Windows 10 的 UWP 中使用以下值更改 ColumnSpan:

<Setter Target="ProgramView.ColumnSpan" Value="{Binding
IsProgramViewVisible, Converter={StaticResource OneIfVisibleConverter}}"/>

我有两个问题:

a) 它不允许我绑定 b) 即使它在我的页面资源中声明,它也找不到转换器。

当我将鼠标移到上面时,它会显示一个错误:

Catastrophic Failure: (Exception from HRESULT: 0X8000FFFF (E_UNEXPECTED)

无论我是否定义我的转换器都会发生错误,所以我假设问题出在绑定上。

有什么方法可以实现吗?

谢谢。

【问题讨论】:

  • 你可能想看看这个blog.galasoft.ch/posts/2012/09/…
  • @bit 谢谢你。我找到了这篇文章(仍然在我的浏览器中打开),但我认为它对于我想要实现的目标来说过于复杂,但我会更详细地阅读它,看看我是否能抓住并理解基本概念并将其应用到我的解决方案。
  • 在该页面上转到 现在是 ColumnSpan! 这很可能是您需要的

标签: c# xaml win-universal-app


【解决方案1】:

由于我们无法在不查看您的代码的情况下提供准确的解决方案。

这是一个检查列表,您可以按照以下步骤找出错误:

1.调试您的转换器并检查它是否为所有测试用例返回所需的值。

2.检查所有名称是否正确,您的 xaml 中是否有任何拼写错误。

这是一个将列 span 与 vm 绑定并使用绑定到 click 事件的命令对其进行更新的实现。

<Page.Resources>
        <local:BoolToColumnSpanConverter x:Key="BoolToColumnSpanConverter" />
    </Page.Resources>
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Grid.DataContext>
            <local:Items />
        </Grid.DataContext>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="100" />
            <ColumnDefinition Width="100" />
            <ColumnDefinition Width="100" />
            <ColumnDefinition Width="100" />
        </Grid.ColumnDefinitions>
        <Rectangle HorizontalAlignment="Stretch"
                      VerticalAlignment="Stretch"
                      Fill="Red"
                      Grid.ColumnSpan="{Binding span, Converter={StaticResource BoolToColumnSpanConverter}}" />
        <Button Click="Button_Click"
                  Content="change span"
                  Grid.ColumnSpan="2"
                  Grid.Column="2" 
                  Margin="5"
                  Command="{Binding ChangeSpanCommand, Mode=OneWay}" />
    </Grid>

后面的代码:

转换器只是将 true 转换为 2,将 false 转换为 1

该命令绑定到 VM 中名为 UpdateSpan 的方法,该方法只是将布尔值反转。

当按钮被按下时,因为它绑定到命令被调用的命令,因为它只是返回一个带有 UpdateSpan 作为参数的新中继命令,这个方法被执行..这将更新触发更改的 span 布尔值,系统通过 OnPropertyChanged 事件通知该更改,并执行值转换器,将列跨度变为 1 和 2。

public class BoolToColumnSpanConverter : IValueConverter
    {
        public object Convert( object value , Type targetType , object parameter , string language )
        {
            var b = (bool)value;

            return b ? 2 : 1;
        }

        public object ConvertBack( object value , Type targetType , object parameter , string language )
        {
            throw new NotImplementedException();
        }
    }


    public class Items : INotifyPropertyChanged
    {
        private bool _span;
        public bool span
        {
            get { return _span; }
            set
            {
                if (value != _span) _span = value;
                OnPropertyChanged();
            }
        }

        public ICommand ChangeSpanCommand {
            get
            {
                return new RelayCommand(() => UpdateSpan());
            }
        }

        public Items()
        {
            span = true;
        }

        public void UpdateSpan()
        {
            span = !span;
        }

        #region Notify Property Changed Members
        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged( [CallerMemberName]string propertyName = null )
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this , new PropertyChangedEventArgs(propertyName));
            }
        }
        #endregion
    }

    public class RelayCommand : ICommand
    {
        public event EventHandler CanExecuteChanged;

        public bool CanExecute( object parameter )
        {
            return true;
        }

        public void Execute( object parameter )
        {
            this._action();
        }

        private Action _action;

        public RelayCommand( Action action )
        {
            this._action = action;
        }
    }

【讨论】:

  • 您好,Akash,感谢您的反馈,对缺少代码表示歉意。我认为提供上述代码就足够了。转换器绝对正确,因为我已经在我的应用程序的其他部分进行了测试,只是为了确保它是正确的。此外,我的属性也是正确的,因为我可以看到它们在我显示它们以进行测试时相应地进行了更改。在阅读了相当多之后,我认为我提供的代码只适用于 WPF 而不是 UWP!我发现一篇文章指出,自 Windows 8 以来,Windows Store 应用程序中不再提供触发器。
  • MSDN 帖子是social.msdn.microsoft.com/Forums/windowsapps/en-US/…,提供的答案之一是我基本上应该使用 VisualState。我会通读这个,看看我是否可以让它以这种方式工作。无论哪种方式,我都会发布更新。
  • Hmm .. 你应该使用可视状态管理器 :) .. 并记住将可视状态管理器放在可视化树的末尾,以确保它不会产生任何错误 .. 一次我遇到了问题,它无法识别树中在其下方声明的文本块名称
【解决方案2】:

这是我的问题的答案。

通用应用程序(Windows 应用商店和 Windows Phone 应用程序)不允许直接在 Setter 中绑定,但可以使用 WPF。

<Setter Target="ProgramView.ColumnSpan" Value="{Binding
IsProgramViewVisible, Converter={StaticResource OneIfVisibleConverter}}"/>

Triggers/DataTriggers 在 XAML 中不受支持,已被 VisualStateManager 取代,如 StackOverflow 中的 DataTriggers in WinRT 帖子中所述。

我找到了有关如何解决此问题的各种解释,因此这里有一些链接可能对您也有帮助:

所以我上面提到的解决方案是使用VisualStateManager,更具体地说,`DataTriggerBehavior.

一个让我花费大量时间的错误是尝试将它与现有的AdaptiveTrigger MinWindowWidth 结合使用,因为我想根据大小和绑定属性设置我的 VisualState。结果证明这是一场噩梦,遗憾的是没有更好的两者混合。也许有一个解决方案,但我仍然缺少一些东西,但现在我的解决方案如下:

  1. 定义我的各种 VisualStates 并在其中设置各种属性:

    <VisualState x:Name="ListOnly">
        <VisualState.Setters>
            <Setter Target="ProgramList.(Grid.Column)" Value="0" />
            ....
        </VisualState.Setters>
     </VisualState>
    
  2. 在 Page_SizeChanged 事件中从 MainPage.Xaml 检查我的应用的方向和宽度,并在 App.cs 中创建一个静态属性并从那里进行设置:

    private void Page_SizeChanged(object sender, SizeChangedEventArgs e)
    {
        if (ApplicationView.GetForCurrentView().Orientation ==
        ApplicationViewOrientation.Landscape)
        {
            App.IsLandscape = (ApplicationView.GetForCurrentView()
            .VisibleBounds.Width < 600) ? false : true;
        }
        else
        {
            App.IsLandscape = false;
        }
    }
    
  3. 例如,从加载在 MainPage 框架中的相关页面,即 ListPage.xaml,我在相关 ViewModel 即 ListPageViewModel 中创建了一个名为 SizeChanged 的​​函数,我从页面的 Page_SizeChange 事件中调用它:

    private void Page_SizeChanged(object sender, Windows.UI.Xaml.SizeChangedEventArgs e)
    {
        this.GetViewModel.SizeChanged();
    }
    
  4. 相关 ViewModel 中的 SizeChange 方法将根据我的应用的方向和大小更改绑定到 DataTrigger 的属性:

    public void SizeChanged()
    {
        if (!App.IsLandscape)
        {
             ...
             this.ActivePart = ActivePartEnum.ListOnly.ToString();
             ...
        }
        else
        {
             ...
             this.ActivePart = ActivePartEnum.Both.ToString();
             ...
        }
    }
    
  5. 最后在相关的XAML Page中,调用DataTriggerBehaviour:

    <Interactivity:Interaction.Behaviors>
        <Core:DataTriggerBehavior Binding="{Binding ActivePart}"
         ComparisonCondition="Equal" Value="ListOnly">
            <Core:GoToStateAction StateName="ListOnly" />
        </Core:DataTriggerBehavior>
        ....
    </Interactivity:Interaction.Behaviors>
    

通过以上内容,您应该能够根据应用的方向和大小应用特定模板,并且可以根据需要进行扩展。我将进一步研究的一件事是行为,但无法让它发挥作用,现在不得不继续前进,但我认为这将是一个更好的解决方案。

希望这对您有所帮助,并感谢大家的反馈。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-04-24
    • 1970-01-01
    • 1970-01-01
    • 2015-11-09
    • 2015-11-04
    • 2016-01-08
    • 1970-01-01
    相关资源
    最近更新 更多