Caliburn.Micro:网格未绑定/链接到x:名称

use*_*952 0 c# xaml caliburn.micro windows-phone-8

所以我正在开发一个带有Caliburn.Micro框架的Windows Phone 8应用程序.我正在尝试创建一个网格,我在运行时在运行时添加/删除TextBlock等元素.我已经尝试了一些东西来将我的代码绑定到x:Name但到目前为止还没有任何工作.

所以我尝试过的一件事就是在我的xaml aka视图中有一个占位符网格:

    <Grid x:Name="ContentPanel" Margin="0,97,0,0" Grid.RowSpan="2">
    </Grid>
Run Code Online (Sandbox Code Playgroud)

然后我的ViewModel使用以下内容绑定我的ContentPanel网格:

    private Grid contentPanel;
    public Grid ContentPanel
    {
        get
        {
            return contentPanel;
        }
        set
        {
            contentPanel = value;
            NotifyOfPropertyChange(() => ContentPanel);
        }
    }
Run Code Online (Sandbox Code Playgroud)

然后我创建了一个TextBlock来添加到网格中:

TextBlock txt1 = new TextBlock();
txt1.Text = "2005 Products Shipped";
txt1.FontSize = 20;
txt1.FontWeight = FontWeights.Bold;
Grid.SetRow(txt1, 1);
Run Code Online (Sandbox Code Playgroud)

最后我将TextBlock添加到我的网格:

ContentPanel.Children.Add(txt1);
Run Code Online (Sandbox Code Playgroud)

当我运行此代码ContentPanel时,结果是等于null,为什么呢?不应该Caliburn自动绑定ContentPanel x:Name="ContentPanel"与属性ContentPanel

我很感激你在这件事上的帮助.

我需要解决的核心问题是: 我在我的应用程序中有一个登录页面,其中显示了从服务器加载的一些图片和文本.如下所示,这是通过Image和TextBlock完成的.当该服务器处于脱机状态或者未启用wi-fi时,我想用静态图像替换此图片+文本.Aka我想从StackPanel中删除TextBlock.

我加载并显示我的服务器的东西的部分很好,在我的xaml中看起来像这样:

<StackPanel Orientation="Horizontal" Background="White" DataContext="{Binding FeedItemsAnnounce,Mode=TwoWay}" >
<Image delay:LowProfileImageLoader.UriSource="{Binding ImagePath,Mode=TwoWay}" Margin="5" Width="170" Height="138">
    <i:Interaction.Triggers>
        <i:EventTrigger
        EventName="Tap">
            <cm:ActionMessage
        MethodName="LoadAnnouncement">
                <cm:Parameter Value="{Binding Link}"></cm:Parameter>
            </cm:ActionMessage>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</Image>
<TextBlock Text="{Binding Title}" TextWrapping="Wrap" Width="160" Foreground="Black" FontSize="24" VerticalAlignment="Center" Margin="25,0,0,0"></TextBlock>
<i:Interaction.Triggers>
    <i:EventTrigger
        EventName="Tap">
        <cm:ActionMessage
        MethodName="LoadAnnouncement">
            <cm:Parameter Value="{Binding Link}"></cm:Parameter>
        </cm:ActionMessage>
    </i:EventTrigger>
</i:Interaction.Triggers>
Run Code Online (Sandbox Code Playgroud)

所以当服务器离线/ wifi禁用时,我想用它替换它.这样TextBlock就不再存在了:

<Image  delay:LowProfileImageLoader.UriSource="{Binding ImagePath,Mode=TwoWay}" DataContext="{Binding FeedItemsAdvertisement,Mode=TwoWay}" Margin="0,20,0,39" Width="380" Height="128">
<i:Interaction.Triggers>
    <i:EventTrigger
        EventName="Tap">
        <cm:ActionMessage
        MethodName="LoadAdvertisement" >
            <cm:Parameter Value="{Binding Link}"></cm:Parameter>
        </cm:ActionMessage>
    </i:EventTrigger>
</i:Interaction.Triggers>
Run Code Online (Sandbox Code Playgroud)

这甚至可能吗?如果不是最好的半解决方案是什么?

编辑1:我已按照接受的答案中的说明设置了流程.但我BooleanToVisibilityConverter没有被召唤,尽管我NotifyOfPropertyChange(() => IsConnectionAvailable);被召唤了.

我的财产:

private bool _isConnectionAvailable;

public bool IsConnectionAvailable
{
    get { return _isConnectionAvailable; }
    set
    {
        if (_isConnectionAvailable != value)
        {
            _isConnectionAvailable = value;
            NotifyOfPropertyChange(() => IsConnectionAvailable);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我如何更改bool:这个代码在我的构造函数中为我的ViewModel调用(就像测试它是否正常工作):

IsConnectionAvailable = false;
Run Code Online (Sandbox Code Playgroud)

TextBlock(没有触发器代码导致它与之前相同):

<TextBlock Text="{Binding Title}" Visibility="{Binding IsConnectionAvailable, Converter={StaticResource BoolToVisibility}}" TextWrapping="Wrap" Width="160" Foreground="Black" FontSize="24" VerticalAlignment="Center" Margin="25,0,0,0"></TextBlock>
Run Code Online (Sandbox Code Playgroud)

这就像是Binding IsConnectionAvailable无法工作,因为我可以将IsConnectionAvailable我的Xaml中的名称更改为任何内容,我NotifyOfPropertyChange(() => IsConnectionAvailable);仍然会被调用.

有任何想法吗?

我甚Visibility="{Binding Path=IsVisibil,Mode=TwoWay}至无法正常绑定public Visibility IsVisibil属性.我在其他课上做过这个,但即使这样也行不通?

编辑2:绑定不起作用的问题,似乎在这段代码中的某处:

<StackPanel Orientation="Horizontal" Background="White" DataContext="{Binding FeedItemsAnnounce,Mode=TwoWay}" >
                <Image delay:LowProfileImageLoader.UriSource="{Binding ImagePath,Mode=TwoWay}" Margin="5" Width="170" Height="138">
                    <i:Interaction.Triggers>
                        <i:EventTrigger
                        EventName="Tap">
                            <cm:ActionMessage
                        MethodName="LoadAnnouncement">
                                <cm:Parameter Value="{Binding Link}"></cm:Parameter>
                            </cm:ActionMessage>
                        </i:EventTrigger>
                    </i:Interaction.Triggers>
                </Image>
                <TextBlock Text="{Binding Title}" Visibility="{Binding Path=IsVisibil,Mode=TwoWay}" TextWrapping="Wrap" Width="160" Foreground="Black" FontSize="24" VerticalAlignment="Center" Margin="25,0,0,0"></TextBlock>
                <i:Interaction.Triggers>
                    <i:EventTrigger
                        EventName="Tap">
                        <cm:ActionMessage
                        MethodName="LoadAnnouncement">
                            <cm:Parameter Value="{Binding Link}"></cm:Parameter>
                        </cm:ActionMessage>
                    </i:EventTrigger>
                </i:Interaction.Triggers>
            </StackPanel>
Run Code Online (Sandbox Code Playgroud)

EDIT 1和2的解决方案:我在xaml结构的顶部创建了一个x:Name"Root".然后将绑定更改为:

ElementName=Root, Path=DataContext.IsVisibil
Run Code Online (Sandbox Code Playgroud)

这是必需的,因为我尝试设置的可见性绑定在另一个DataContxt中.

Cha*_*leh 5

这不是使用CM的正确方法,在许多方面您会混淆CM中的模型和视图模型以及绑定功能.

你目前在做什么

您正在尝试让CM框架查找ContentPanel在ViewModel上调用的属性,并自动确定Grid要将其绑定到的属性...

由于以下几个原因,这不起作用:

  1. 我不认为CM中有一个Grid的约定 - 它不是一个明显的可绑定方式(它是一个布局容器)
  2. Grid不是一个支持数据的控件 - 它不知道如何使用集合并在框外显示动态行(它是一个布局容器)
  3. 你正在做的事情没有任何意义(你的UserControl中有一个网格实例,你还在ViewModel中实例化了一个网格 - 这是控件的两个独立实例 - 你不能'绑定'它们在一起 - 这不是一切都有效)

CM和绑定

当您使用元素名称绑定(例如x:Name使用CM)时,它会尝试在ViewModel上找到与元素名称匹配的属性.此时,根据所讨论的源控件的约定设置,CM将尝试自动连接所有的零碎.

有一些默认约定,ConventionManager其中确定在使用元素名称绑定时要绑定TextBlockText属性- 例如,for上的属性TextBlock绑定到ViewModel上的target属性.

http://caliburnmicro.codeplex.com/SourceControl/latest#src/Caliburn.Micro.Platform/ConventionManager.cs - 查看ConventionManager上的类构造函数,以查看开箱即用的约定 - 没有一个用于Grid

找到目标属性后,CM会将其绑定.

(顺便说一下:值得注意的是,如果控件类型是一个ContentControlCM会做一些组合魔术,所以你可以拥有包含其他视图模型的视图模型,并且在运行时都有一个组合 - 非常适合具有多个子窗口的屏幕等)

您遇到的问题是没有Grid开箱即用的常规设置- 这很可能是因为GridSL/WPF中的主要用于布局,并且实际上不是"数据容器"或以任何方式识别数据(分开)从您可以绑定的少数依赖属性) - 即我不认为可以绑定到网格并获得动态数量的列/行而无需对控件进行一些自定义,因此省略了任何约定

(考虑一下 - 如果你将网格绑定到一个集合,网格应该做什么...添加行或列?它不能以合理的方式支持它)

现在将它带回SL/WPF一秒钟:

通常,如果您需要项目的变量列表,则需要绑定到ItemsSourceItemsControl(或ItemsControl自身)继承的控件的属性.

许多控件执行此操作:如果他们需要显示动态数量的项目,他们通常会继承ItemsControl.

这与CM有什么关系?

Caliburn Micro知道如何ItemsControl开箱即用.这意味着您可以在ViewModel上包含一个包含项集合的属性,并在绑定后在运行时获得这些属性的动态视图

例如 - CM绑定ItemsControl可能如下所示:

<ItemsControl x:Name="TextItems">
  <!-- host the items generated by this ItemsControl in a grid -->
  <ItemsControl.ItemsPanel>
    <ItemsPanelTemplate>
      <Grid/>
    </ItemsPanelTemplate>
  </ItemsControl.ItemsPanel>
  <!-- render each bound item using a TextBlock-->
  <ItemsControl.ItemTemplate>
    <DataTemplate>
      <TextBlock Text="{Binding SomeTextualProperty}"/>
    </DataTemplate>
  </ItemsControl.ItemTemplate>
</ItemsControl>
Run Code Online (Sandbox Code Playgroud)

现在你只需要一个对象集合来绑定它 - 集合中的每个项目都成为控件中的一个新项目,它DataContext指向绑定项目.我假设你希望每个项目都是一个包含属性的ViewModel SomeTextualProperty- 我在这里定义了......

// Provides a viewmodel for a textual item
public class TextItemViewModel
{
    public string SomeTextualProperty { get; set;}
}
Run Code Online (Sandbox Code Playgroud)

应包含项列表的VM需要具有要绑定的集合.

(注意:由于您在运行时向其添加项目,因此您需要在集合更改时告知UI - ObservableCollection在实现集合更改通知事件时免费提供此功能)

// This is the viewmodel that contains the list of text items
public class ScreenViewModel 
{
    public ObservableCollection<TextItemViewModel> TextItems { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

还有什么我会考虑不正确的方法

您的ViewModel不应该知道您的View实现,即他们不应该引用任何类型的控件,除非绝对必要(我不能想到我必须将控件放在VM中的时间).ViewModels应该对视图进行建模 - 但是他们不应该真正需要知道该视图包含的任何细节 - 这样它们更容易测试并且很容易被重用

如果您遵循上述方法,您可以提供一个重用视图模型的应用程序,但为每个视图模型提供不同的视图.您可以通过替换ItemsControl视图中的其他类型的控件来尝试此操作(只要它具有数据感知功能,例如数据网格),并且VM仍然可以工作 - VM是视图无关的.

Grid在VM中的使用并不理想,因为它Grid是一种可视化控件,它不是数据.请记住,视觉效果是您的View,ViewModel应该只包含数据和事件,通知视图发生的事情

如果我这样做 - 代码看起来更像我上面发布的代码.

总结一下

  • 对要在ViewModel(TextItemViewModel)中显示的信息建模
  • ScreenViewModel使用更改感知集合(例如,)将这些对象的集合添加到主ViewModel()ObservableCollection
  • 使用标准添加/删除从集合中添加/删除项目
  • 绑定ItemsControl使用视图x:Name绑定在你的收藏ScreenViewModel
  • 添加/删除VM中的项目将触发属性更改通知.ItemsControl将观察这些事件并相应地更新自己

附录

你可以使用a ObservableCollection<string>而不是a来逃避TextBlockViewModel但是不清楚你是否想要为绑定到网格的项添加更多属性(例如IsHeading标题的属性,然后你可以在视图中使用粗体/斜体)

如果你只是想使用strings只是修改DataTemplate直接绑定到DataContext而不是一个属性DataContext

<ItemsControl x:Name="TextItems">
  <!-- host the items generated by this ItemsControl in a grid -->
  <ItemsControl.ItemsPanel>
    <ItemsPanelTemplate>
      <Grid/>
    </ItemsPanelTemplate>
  </ItemsControl.ItemsPanel>
  <!-- render each bound item using a TextBlock-->
  <ItemsControl.ItemTemplate>
    <DataTemplate>
      **<TextBlock Text="{Binding}"/> <!-- Bind direct -->**
    </DataTemplate>
  </ItemsControl.ItemTemplate>
</ItemsControl>
Run Code Online (Sandbox Code Playgroud)

编辑:

在你的情况下,它非常简单 - 你的ViewModel应该只是模拟服务器的状态:

public class LoginPageViewModel
{
    public bool IsConnectionAvailable { get; set; } // or whatever your variable should be called
}
Run Code Online (Sandbox Code Playgroud)

然后使用转换器将文本块的可见性绑定到此:

<TextBlock Visibility="{Binding IsConnectionAvailable, Converter={StaticResource BooleanToVisibilityConverter}}">
Run Code Online (Sandbox Code Playgroud)

您需要在某处声明转换器的静态资源(例如在控件本身或主资源字典中)

看起来在System.Windows.Controls某个地方已经定义了一个转换器,但是如果你找不到它,那么实现就非常简单了(你可能做得更好一点,以防止无效输入,但为了简洁,我保持它很小) :

public class BooleanToVisibilityConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (bool) value ? Visibility.Visible : Visibility.Collapsed;
    }

    public object ConvertBack(object value, Type targetType, object parameter,CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}
Run Code Online (Sandbox Code Playgroud)

您可能还希望在视图生命周期中将状态从可用/不可用更改,因此在这种情况下,您可能希望使用内置的属性更改事件PropertyChangedBase(Screen也继承)以使视图知道属性何时更改

    private bool _isConnectionAvailable;

    public bool IsConnectionAvailable
    {
        get { return _isConnectionAvailable; }
        set
        {
            if (_isConnectionAvailable != value)
            {
                _isConnectionAvailable = value;
                NotifyOfPropertyChange(() => IsConnectionAvailable);
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

附录2

我更喜欢简洁的CM语法,而不是在绑定动作消息时显式 - 所以你的XAML会改变:

<Image delay:LowProfileImageLoader.UriSource="{Binding ImagePath,Mode=TwoWay}" DataContext="{Binding FeedItemsAdvertisement,Mode=TwoWay}" Margin="0,20,0,39" Width="380" Height="128">
<i:Interaction.Triggers>
    <i:EventTrigger
        EventName="Tap">
        <cm:ActionMessage
        MethodName="LoadAdvertisement" >
            <cm:Parameter Value="{Binding Link}"></cm:Parameter>
        </cm:ActionMessage>
    </i:EventTrigger>
</i:Interaction.Triggers>
</Image>
Run Code Online (Sandbox Code Playgroud)

<Image delay:LowProfileImageLoader.UriSource="{Binding ImagePath,Mode=TwoWay}" DataContext="{Binding FeedItemsAdvertisement,Mode=TwoWay}" Margin="0,20,0,39" Width="380" Height="128" cal:Message.Attach="[Tap] = [LoadAdvertisement($dataContext.Link)]"></Image>
Run Code Online (Sandbox Code Playgroud)

(实际上,这可能与$ dataContext.Link部分不对......但是它可能会再次......请参阅:http://caliburnmicro.codeplex.com/wikipage?title = All%20About%20Actions&referringTitle=Documentation )