如何DynamicResources建立及其在contextmenus使用 [英] How are DynamicResources built and their use in contextmenus

查看:281
本文介绍了如何DynamicResources建立及其在contextmenus使用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

是动态的资源真正的动态?如果我定义了一个DynamicResource,我意识到一个表达式创建(在哪里?)没有转化为资源,直到运行时,但是,我做的不是understans这是dynamicresouce,一旦建成,现在是否静态

Are dynamic resources truly dynamic? If I define a DynamicResource, I realise that an expression is created (where?) that is not translated into a resource until runtime, however, What I do not understans is whether this dynamicresouce, once built, is now "Static"

举例来说,如果我通过dynamicresource创建上下文菜单,是设置在运行时创建的访问,则静态的菜单项,即使他们必然?

For instance, if I create a context menu via a dynamicresource, are the menuitems which are created at runtime on access then static, even if they are bound?

如果是这样,我怎么可以创建XAML动态的上下文菜单?

If so, how can i create a dynamic context menu in XAML?

推荐答案

这是一个非常复杂的问题,因为有内WPF这么多种类的活力。我先从一个简单的例子来帮助你了解你需要一些基本的概念,然后进行解释,其中一个文本菜单可以动态更新和/或更换,DynamicResource如何融入画面的各种方式。

This is a very complex subject because there are so many kinds of dynamism within WPF. I will start with a simple example to help you understand some basic concepts you need, then proceed to explain the various ways in which a ContextMenu can be dynamically updated and/or replaced, and how DynamicResource fits into the picture.

初始例如:动态更新的文本菜单,通过静态资源引用

假设你有以下内容:

<Window>
  <Window.Resources>
    <ContextMenu x:Key="Vegetables">
      <MenuItem Header="Broccoli" />
      <MenuItem Header="Cucumber" />
      <MenuItem Header="Cauliflower" />
    </ContextMenu>
  </Window.Resources>

  <Grid>
    <Ellipse ContextMenu="{StaticResource Vegetables}" />
    <TextBox ContextMenu="{StaticResource Vegetables}" ... />
    ...
  </Grid>
</Window>



**注意使用静态资源的现在

这XAML将:


  • 构造一个文本菜单有三个的MenuItems对象并把它添加到Window.Resources

  • 用的文本菜单

  • 参考构造一个椭圆对象构造一个TextBox对象通过对文本菜单<参考/ LI>
  • Construct a ContextMenu object with three MenuItems and add it to Window.Resources
  • Construct an Ellipse object with a reference to the ContextMenu
  • Construct a TextBox object with a reference to the ContextMenu

由于两个椭圆和文本框有,在同一文本菜单引用更新文本菜单将改变每个可用的选项。例如,下面将新增胡萝卜,以一个按钮被点击时的菜单。

Since both the Ellipse and the TextBox have references to the same ContextMenu, updating the ContextMenu will change the options available on each. For example the following will add "Carrots" to the menu when a button is clicked.

public void Button_Click(object sender, EventArgs e)
{
  var menu = (ContextMenu)Resources["Vegetables"];
  menu.Items.Add(new MenuItem { Header = "Carrots" });
}

在这个意义上的每个文本菜单是动态的:其项目可以在任何时间进行修改所做的更改将立即生效。这是真实的,即使当文本菜单实际上是开放的(掉了下来)在屏幕上。

In this sense every ContextMenu is dynamic: Its items can be modified at any time and the changes will immediately take effect. This is true even when the ContextMenu is actually open (dropped down) on the screen.

动态文本菜单通过数据更新绑定

的另一种方式,其中单个ContextMenu对象是动态的是,它响应数据绑定。相反,设定个人的MenuItems的可以绑定到一个集合,例如:

Another way in which a single ContextMenu object is dynamic is that it responds to data binding. Instead of setting individual MenuItems you can bind to a collection, for example:

<Window.Resources>
  <ContextMenu x:Key="Vegetables" ItemsSource="{Binding VegetableList}" />
</Window.Resources>

这假定VegetableList被声明为一个ObservableCollection或任何实现INotifyCollectionChanged接口的其他类型。您对收集的任何更改都会即时更新的文本菜单,即使是开放的。例如:

This assumes VegetableList is declared as an ObservableCollection or some other type that implements the INotifyCollectionChanged interface. Any changes you make to the collection will instantly update the ContextMenu, even if it is open. For example:

public void Button_Click(object sender, EventArgs e)
{
  VegetableList.Add("Carrots");
}

请注意,这种集合更新不需要在代码中进行:您可以也蔬菜列表绑定到ListView,数据网格等以便改变可以由最终用户进行。这些变化也将在您的文本菜单显示。

Note that this kind of collection update need not be made in code: You can also bind the vegetable list to a ListView, DataGrid, etc so that changes may be made by the end-user. These changes will also show up in your ContextMenu.

使用代码转换ContextMenus

你也可以用完全不同的文本菜单替换项目的文本菜单。例如:

You can also replace the ContextMenu of an item with a completely different ContextMenu. For example:

<Window>
  <Window.Resources>
    <ContextMenu x:Key="Vegetables">
      <MenuItem Header="Broccoli" />
      <MenuItem Header="Cucumber" />
    </ContextMenu>
    <ContextMenu x:Key="Fruits">
      <MenuItem Header="Apple" />
      <MenuItem Header="Banana" />
    </ContextMenu>
  </Window.Resources>

  <Grid>
    <Ellipse x:Name="Oval" ContextMenu="{StaticResource Vegetables}" />
    ...
  </Grid>
</Window>



菜单可以用代码替换:

The menu can be replaced in code like this:

public void Button_Click(object sender, EventArgs e)
{
  Oval.ContextMenu = (ContextMenu)Resources.Find("Fruits");
}

请注意,而不是修改现有的文本菜单,我们切换到一个完全不同的ContextMenu 。在这种情况下这两个ContextMenus立即建立时,首先构建了窗口,但不使用水果菜单,直到它被切换。

Note that instead of modifying the existing ContextMenu we are switching to a completely different ContextMenu. In this situation both ContextMenus are built immediately when the window is first constructed, but the Fruits menu is not used until it is switched.

如果你想避免构建水果菜单中,直到有必要,你可以在Button_Click处理程序构建它,而不是在XAML做的:

If you want to avoid constructing the Fruits menu until it was necessary you could construct it in the Button_Click handler instead of doing it in XAML:

public void Button_Click(object sender, EventArgs e)
{
  Oval.ContextMenu =
    new ContextMenu { ItemsSource = new[] { "Apples", "Bananas" } };
}

在这个例子中,每次点击按钮新的ContextMenu会构造并分配到椭圆形。在Window.Resources定义的任何文本菜单仍然存在,但是未使用(除非另一个控制使用它)。

In this example, every time you click on the button a new ContextMenu will be constructed and assigned to the oval. Any ContextMenu defined in Window.Resources still exists but is unused (unless another control uses it).

切换使用DynamicResource ContextMenus

使用DynamicResource可以让你没有明确指定其代码ContextMenus之间切换。例如:

Using DynamicResource allows you to switch between ContextMenus without explicitly assigning it code. For example:

<Window>
  <Window.Resources>
    <ContextMenu x:Key="Vegetables">
      <MenuItem Header="Broccoli" />
      <MenuItem Header="Cucumber" />
    </ContextMenu>
  </Window.Resources>

  <Grid>
    <Ellipse ContextMenu="{DynamicResource Vegetables}" />
    ...
  </Grid>
</Window>

由于此XAML使用DynamicResource替代StaticResource的,修改字典将更新椭圆的ContextMenu属性。例如:

Because this XAML uses DynamicResource instead of StaticResource, modifying the dictionary will update the ContextMenu property of the Ellipse. For example:

public void Button_Click(object sender, EventArgs e)
{
  Resources["Vegetables"] =
    new ContextMenu { ItemsSource = new[] {"Zucchini", "Tomatoes"} };
}



关键概念,这里要说的是DynamicResource VS静态资源的如果词典的查找完成控制。如果静态资源在上面的例子中,分配给资源[蔬菜] 将不会更新椭圆的ContextMenu属性。

The key concept here is that DynamicResource vs StaticResource only controls when the dictionary is lookup is done. If StaticResource is used in the above example, assigning to Resources["Vegetables"] will not update the Ellipse's ContextMenu property.

在另一方面,如果你正在更新的文本菜单本身(通过改变它的项目集合或通过数据绑定),它无关紧要是否使用DynamicResource或静态资源:在每种情况下您对文本菜单的变化将是立即可见。

On the other hand, if you are updating the ContextMenu itself (by changing its Items collection or via data binding), it does not matter whether you use DynamicResource or StaticResource: In each case any changes you make to the ContextMenu will be immediately visible.

更新使用数据绑定个人文本菜单项

最好的方法基于是右键单击的项的属性更新文本菜单是使用数据绑定:

The very best way to update a ContextMenu based on properties of the item that is right-clicked is to use data binding:

<ContextMenu x:Key="SelfUpdatingMenu">
  <MenuItem Header="Delete" IsEnabled="{Binding IsDeletable}" />
    ...
</ContextMenu>

这将导致删除菜单项,自动变灰,除非该项目有其IsDeletable标志组。没有代码是在这种情况下,必要的(甚至是可取的)

This will cause the "Delete" menu item to be automatically grayed out unless the item has its IsDeletable flag set. No code is necessary (or even desirable) in this case.

如果你想隐藏的项目,而不是简单地花白出来,设置可见性,而不是IsEnabled:

If you want to hide the item instead of simply graying it out, set Visibility instead of IsEnabled:

<MenuItem Header="Delete"
          Visibility="{Binding IsDeletable, Converter={x:Static BooleanToVisibilityConverter}}" />

如果您要添加/根据你的数据从文本菜单中删除项目,可以绑定使用CompositeCollection。语法是更复杂一点,但它仍然是相当简单:

If you want to add/remove items from a ContextMenu based on your data, you can bind using a CompositeCollection. The syntax is a bit more complex, but it is still quite straightforward:

<ContextMenu x:Key="MenuWithEmbeddedList">
  <ContextMenu.ItemsSource>
    <CompositeCollection>
      <MenuItem Header="This item is always present" />
      <MenuItem Header="So is this one" />
      <Separator /> <!-- draw a bar -->
      <CollectionContainer Collection="{Binding MyChoicesList}" />
      <Separator />
      <MenuItem Header="Fixed item at bottom of menu" />
    </CompositeCollection>
  </ContextMenu.ItemsSource>
</ContextMenu>



假设MyChoicesList是一个ObservableCollection(或实现INotifyCollectionChanged其他类),项目添加/删除/在此集合更新将在文本菜单立即可见。

Assuming "MyChoicesList" is an ObservableCollection (or any other class that implements INotifyCollectionChanged), items added/removed/updated in this collection will be immediately visible on the ContextMenu.

更新个别文本菜单项目,而不数据绑定

在所有可能的你应该使用数据绑定控制你的ContextMenu项目。他们的工作非常出色,几乎是万无一失的,并大大简化你的代码。只有当数据绑定无法进行工作是否有意义使用代码来更新你的菜单项。在这种情况下,你可以通过处理ContextMenu.Opened事件,此事件中做更新,建立自己的ContextMenu。例如:

When at all possible you should control your ContextMenu items using data binding. They work very well, are nearly foolproof, and greatly simplify your code. Only if data binding can't be made to work does it make sense to use code to update your menu items. In this case you can build your ContextMenu by handling the ContextMenu.Opened event and doing updates within this event. For example:

<ContextMenu x:Key="Vegetables" Opened="Vegetables_Opened">
  <MenuItem Header="Broccoli" />
  <MenuItem Header="Green Peppers" />
</ContextMenu>



有了这个代码:

With this code:

public void Vegetables_Opened(object sender, RoutedEventArgs e)
{
  var menu = (ContextMenu)sender;
  var data = (MyDataClass)menu.DataContext

  var oldCarrots = (
    from item in menu.Items
    where (string)item.Header=="Carrots"
    select item
  ).FirstOrDefault();

  if(oldCarrots!=null)
    menu.Items.Remove(oldCarrots);

  if(ComplexCalculationOnDataItem(data) && UnrelatedCondition())
    menu.Items.Add(new MenuItem { Header = "Carrots" });
}



另外这个代码可以简单地改变 menu.ItemsSource 如果你使用数据绑定。

Alternatively this code could simply change menu.ItemsSource if you were using data binding.

<使用触发器STRONG>开关ContextMenus

常用来更新ContextMenus的另一种技术是使用一个触发器或DataTrigger至默认上下文菜单,并根据不同的触发条件的自定义上下文菜单之间切换。这可以处理,你要使用数据绑定,但需要更换菜单作为一个整体,而不是它的更新部分的情况。

Another technique commonly used to update ContextMenus is to use a Trigger or DataTrigger to switch between a default context menu and a custom context menu depending on the triggering condition. This can handle situations where you want to use data binding but need to replace the menu as a whole rather than update parts of it.

下面就是这个外观的说明这样的:

Here is an illustration of what this looks like:

<ControlTemplate ...>

  <ControlTemplate.Resources>
    <ContextMenu x:Key="NormalMenu">
      ...
    </ContextMenu>
    <ContextMenu x:Key="AlternateMenu">
      ...
    </ContextMenu>
  </ControlTemplate.Resources>

  ...

  <ListBox x:Name="MyList" ContextMenu="{StaticResource NormalMenu}">

  ...

  <ControlTemplate.Triggers>
    <Trigger Property="IsSpecialSomethingOrOther" Value="True">
      <Setter TargetName="MyList" Property="ContextMenu" Value="{StaticResource AlternateMenu}" />
    </Trigger>
  </ControlTemplate.Triggers>
</ControlTemplate>

在这种情况下,仍然可以使用的数据绑定,以控制在两个NormalMenu和AlternateMenu单个项。

In this scenario it is still possible to use data binding to control individual items in both NormalMenu and AlternateMenu.

释放的ContextMenu资源时菜单关闭

如果在使用的资源文本菜单是昂贵的,以保持在RAM中你可能想释放他们。如果您正在使用数据绑定这很可能是自动发生,因为当菜单关闭在DataContext被删除。如果你正在使用代码,而不是你可能要捉对文本菜单Closed事件来释放任何你响应打开事件创建。

If resources used in a ContextMenu are expensive to keep in RAM you may want to release them. If you are using data binding this is likely to happen automatically, as the DataContext is removed when the menu is closed. If you are using code instead you may have to catch the Closed event on the ContextMenu to deallocate whatever you created in response to the Opened event.

延迟建设从文本菜单XAML

如果您有想在XAML的代码,但不希望在需要时,除了加载一个非常复杂的ContextMenu,二基本技术可供选择:

If you have a very complex ContextMenu that you want to code in XAML but don't want to load except when it is needed, two basic techniques are available:


  1. 把它放在一个单独的ResourceDictionary。必要时,装入的ResourceDictionary并将其添加到MergedDictionaries。只要你使用DynamicResource,合并后的价值将有所回升。

  1. Put it in a separate ResourceDictionary. When necessary, load that ResourceDictionary and add it to MergedDictionaries. As long as you used DynamicResource, the merged value will be picked up.

放在一个的ControlTemplate或DataTemplate中。菜单实际上不会被初始化,直到第一次使用的模板。

Put it in a ControlTemplate or DataTemplate. The menu will not actually be instantiated until the template is first used.

但本身没有这些技术将导致当上下文菜单发生装载的打开的 - 只有当含有模板实例或字典合并。为了实现这个目标,你必须使用一个文本菜单有一个空的ItemsSource然后在打开的情况下分配的ItemsSource。然而ItemsSource的价值可以从一个的ResourceDictionary在一个单独的文件中加载:

However neither of these techniques by itself will cause the loading to happen when the context menu is opened - only when the containing template is instantiated or the dictionary is merged. To accomplish that you must use a ContextMenu with an empty ItemsSource then assign the ItemsSource in the Opened event. However the value of the ItemsSource can be loaded from a ResourceDictionary in a separate file:

<ResourceDictionary ...>
  <x:Array x:Key="ComplexContextMenuContents">
    <MenuItem Header="Broccoli" />
    <MenuItem Header="Green Beans" />
    ... complex content here ...
  </x:Array>
</ResourceDictionary>



这段代码在开业活动:

with this code in the Opened event:

var dict = (ResourceDictionary)Application.LoadComponent(...);
menu.ItemsSource = dict["ComplexMenuContents"];

和这个代码在Closed事件:

and this code in the Closed event:

menu.ItemsSource = null;



其实,如果你只有一个x:数组,你不妨跳过ResourceDictionary中。如果你的XAML的最外层的元素是x:Array中打开事件代码很简单:

Actually if you have only a single x:Array, you may as well skip the ResourceDictionary. If your XAML's outermost element is the x:Array the Opened event code is simply:

menu.ItemsSource = Application.LoadComponent(....)

的关键概念摘要

DynamicResource仅用于在此基础上的资源字典装载和它们包含的内容切换值:当更新字典的内容,DynamicResource自动更新的属性。静态资源只读取他们在XAML加载。

DynamicResource is used only for switching values based on which resource dictionaries are loaded and what they contain: When updating the contents of the dictionaries, DynamicResource automatically updates the properties. StaticResource only reads them when the XAML is loaded.

无论DynamicResource或静态资源是否被使用,该文本菜单中创建的当资源字典加载不是当菜单被打开。

No matter whether DynamicResource or StaticResource is used, the ContextMenu is created when the resource dictionary is loaded not when the menu is opened.

ContextMenus非常动态的,因为你可以操纵他们使用数据绑定或代码所做的更改将立即生效。

ContextMenus are very dynamic in that you can manipulate them using data binding or code and the changes immediately take effect.

在大多数情况下,你应该更新您的ContextMenu使用数据绑定,而不是代码。

In most cases you should update your ContextMenu using data bindings, not in code.

完全替换菜单可以用代码实现,触发器,或DynamicResource

Completely replacing menus can be done with code, triggers, or DynamicResource.

如果内容必须被加载到RAM只有当菜单是开放的,可以将它们从打开事件的单独文件加载并在清除出Closed事件。

If contents must be loaded into RAM only when the menu is open, they can be loaded from a separate file in the Opened event and cleared out in the Closed event.

这篇关于如何DynamicResources建立及其在contextmenus使用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆