所以我正在使用Caliburn.Micro框架开发Windows Phone 8应用程序。我正在尝试创建一个网格,我在运行时在运行时添加/删除TextBlock等元素。我已经尝试了一些方法将我的代码绑定到x:Name但到目前为止还没有任何工作。
所以我尝试过的一件事就是在我的xaml中有一个占位符网格又名视图:
<Grid x:Name="ContentPanel" Margin="0,97,0,0" Grid.RowSpan="2">
</Grid>
然后我的ViewModel使用以下内容来绑定我的ContentPanel网格:
private Grid contentPanel;
public Grid ContentPanel
{
get
{
return contentPanel;
}
set
{
contentPanel = value;
NotifyOfPropertyChange(() => ContentPanel);
}
}
然后我创建了一个TextBlock来添加到网格中:
TextBlock txt1 = new TextBlock();
txt1.Text = "2005 Products Shipped";
txt1.FontSize = 20;
txt1.FontWeight = FontWeights.Bold;
Grid.SetRow(txt1, 1);
最后我将TextBlock添加到我的网格中:
ContentPanel.Children.Add(txt1);
当我运行此代码ContentPanel
时,结果是等于null
,为什么会这样?不应该Caliburn使用属性x:Name="ContentPanel"
自动绑定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>
因此,当服务器处于脱机状态/禁用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>
这甚至可能吗?如果不是最好的半解决方案是什么?
编辑1:我已设法按照接受的答案中的说明设置流程。但我的BooleanToVisibilityConverter
未被调用,但我的NotifyOfPropertyChange(() => IsConnectionAvailable);
被调用。
我的财产:
private bool _isConnectionAvailable;
public bool IsConnectionAvailable
{
get { return _isConnectionAvailable; }
set
{
if (_isConnectionAvailable != value)
{
_isConnectionAvailable = value;
NotifyOfPropertyChange(() => IsConnectionAvailable);
}
}
}
我如何更改bool:在我的构造函数中为我的ViewModel调用此代码(只是作为测试它是否正常工作):
IsConnectionAvailable = false;
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>
就像Binding IsConnectionAvailable
没有效果一样,因为我可以将我的Xaml中的名称IsConnectionAvailable
更改为任何内容,我的NotifyOfPropertyChange(() => IsConnectionAvailable);
仍会被调用。< / p>
有什么想法吗?
我甚至无法对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>
EDIT 1和2的解决方案:我创建了一个x:Name&#34; Root&#34;在我的xaml结构的顶部。然后将绑定更改为:
ElementName=Root, Path=DataContext.IsVisibil
这是必需的,因为我尝试设置的可见性绑定是在另一个DataContxt内。
答案 0 :(得分:5)
这不是使用CM的正确方法,在许多方面您会混淆CM中的模型和视图模型以及绑定功能。
您目前正在做什么
您正在尝试让CM框架在ViewModel上查找名为ContentPanel
的属性,并自动找出Grid
上的哪些属性将其绑定到...
由于以下几个原因,这不会起作用:
CM和Bindings
使用元素名称绑定时,例如使用CM x:Name
,它会尝试在ViewModel上找到与元素名称匹配的属性。此时,根据相关源控件的约定设置,CM将尝试自动连接所有的碎片。
ConventionManager
中包含默认约定,用于确定在使用元素名称绑定时要绑定的属性 - 例如对于TextBlock
,Text
上的TextBlock
属性绑定到ViewModel上的目标属性。
http://caliburnmicro.codeplex.com/SourceControl/latest#src/Caliburn.Micro.Platform/ConventionManager.cs - 查看ConventionManager上的类构造函数,以查看开箱即用的约定 - Grid
不适用
找到目标属性后,CM会将其绑定。
(顺便说一句:值得注意的是,如果控件类型为ContentControl
CM,则会执行一些组合魔术,因此您可以拥有包含其他视图模型的视图模型,并且具有所有视图模型在运行时绑定 - 非常适用于具有多个子窗口的屏幕等)
你遇到的问题是Grid
没有开箱即用的常规设置 - 这很可能是因为SL / WPF中的Grid
主要用于布局,并不是真的一个数据容器&#39;或以任何方式识别数据(除了可以绑定的少数依赖属性) - 即我不认为可以绑定到网格并获得动态数量的列/行而无需进行一些定制对控制,因此省略任何惯例
(考虑一下 - 如果你将一个网格绑定到一个集合,那么网格应该做什么......添加行或列?它不能以合理的方式得到支持)
现在将它带回SL / WPF一秒钟:
通常,如果你想要一个变量的项目列表,你需要绑定到一个继承自ItemsSource
(或ItemsControl
本身)的控件的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>
现在您只需要一个对象集合来绑定它 - 集合中的每个项目都成为控件中的一个新项目,其DataContext
指向绑定项目。我已经假设你希望每个项目都是一个包含属性SomeTextualProperty
的ViewModel - 我已经在这里定义了......
// Provides a viewmodel for a textual item
public class TextItemViewModel
{
public string SomeTextualProperty { get; set;}
}
应包含项目列表的VM需要具有要绑定的集合。
(注意:由于您在运行时向其添加项目,因此您需要在集合更改时告知UI - ObservableCollection
免费为您提供此项,因为它实现了集合更改的通知事件)
// This is the viewmodel that contains the list of text items
public class ScreenViewModel
{
public ObservableCollection<TextItemViewModel> TextItems { get; set; }
}
我还会考虑不正确的方法
您的ViewModel不应该知道您的View实现,即除非绝对必要,否则他们不应该引用任何类型的控件(我无法想到我必须将控件放入控件的时间VM)。 ViewModels应该对视图进行建模 - 但是他们不应该真正了解该视图包含的任何细节 - 这样它们更容易测试并且很容易被重用
如果您遵循上述方法,您可以提供一个重用视图模型的应用程序,但为每个视图模型提供不同的视图。您可以通过在视图中将ItemsControl
替换为其他类型的控件来尝试此操作(只要它具有数据感知功能,例如数据网格),并且VM仍可正常工作 - 虚拟机与视图无关。
您在VM中使用Grid
并不理想,因为Grid
是可视控件,而不是数据。请注意,视觉效果是您的View
,而ViewModel
应该只包含通知视图发生事件的数据和事件
如果我这样做 - 代码看起来更像我上面发布的代码。
总结
TextItemViewModel
)ScreenViewModel
ObservableCollection
)
ItemsControl
绑定将视图中的x:Name
绑定到ScreenViewModel
上的集合ItemsControl
会关注这些事件并相应地自行更新<强>附录强>
您只需使用ObservableCollection<string>
代替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>
修改强>
好的,在你的情况下它非常简单 - 你的ViewModel应该只是模拟服务器的状态:
public class LoginPageViewModel
{
public bool IsConnectionAvailable { get; set; } // or whatever your variable should be called
}
然后使用转换器将文本块的可见性绑定到此:
<TextBlock Visibility="{Binding IsConnectionAvailable, Converter={StaticResource BooleanToVisibilityConverter}}">
您需要在某处声明转换器的静态资源(例如在控件本身或主资源字典中)
看起来某个地方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();
}
}
您可能还希望在视图生命周期中更改状态从可用/不可用,因此在这种情况下,您可能希望使用内置于PropertyChangedBase
的属性更改事件(Screen
也继承)让视图知道属性何时发生变化
private bool _isConnectionAvailable;
public bool IsConnectionAvailable
{
get { return _isConnectionAvailable; }
set
{
if (_isConnectionAvailable != value)
{
_isConnectionAvailable = value;
NotifyOfPropertyChange(() => IsConnectionAvailable);
}
}
}
附录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>
要
<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>
(实际上这可能与$ dataContext.Link部分不对......但是它可能会再次......看到这里:http://caliburnmicro.codeplex.com/wikipage?title=All%20About%20Actions&referringTitle=Documentation)