UWP CommandBar绑定

时间:2018-03-22 08:27:07

标签: c# uwp navigationview commandbar

是否可以将UWP CommandBar绑定到类似ObservableCollection左右的位置?

我想要实现的目的是将我的CommandBar NavigationView绑定到特定Page的对象,以便AppBarButton动态变化,具体取决于当前{ {1}}

我尝试了什么:

MainPage.xaml中

Page

SomePage.xaml.cs

    <NavigationView.HeaderTemplate>
        <DataTemplate>
            <Grid>
                <CommandBar Grid.Column="1"
                        HorizontalAlignment="Right"
                        VerticalAlignment="Top"
                        DefaultLabelPosition="Right"
                        Background="{ThemeResource SystemControlBackgroundAltHighBrush}"  Content="{Binding Path=Content.AppBarButtonList, ElementName=rootFrame}">
                </CommandBar>
            </Grid>
        </DataTemplate>
    </NavigationView.HeaderTemplate>

public ObservableCollection<AppBarButton> AppBarButtonList = new ObservableCollection<AppBarButton> { new AppBarButton { Icon = new SymbolIcon(Symbol.Accept), Label="Bla" }, new AppBarButton{Icon=new SymbolIcon(Symbol.Add),Label="Add"} }; 没有显示任何内容。

感谢。

2 个答案:

答案 0 :(得分:1)

我原来的解决方案是使用PrimaryCommands属性来绑定命令,但事实证明这个属性是只读的。

我对问题的解决方案是使用行为。

首先从NuGet添加对Microsoft.Xaml.Behaviors.Uwp.Managed的引用。

然后将以下行为添加到您的项目中:

public class BindableCommandBarBehavior : Behavior<CommandBar>
{
    public ObservableCollection<AppBarButton> PrimaryCommands
    {
        get { return (ObservableCollection<AppBarButton>)GetValue(PrimaryCommandsProperty); }
        set { SetValue(PrimaryCommandsProperty, value); }
    }

    public static readonly DependencyProperty PrimaryCommandsProperty = DependencyProperty.Register(
        "PrimaryCommands", typeof(ObservableCollection<AppBarButton>), typeof(BindableCommandBarBehavior), new PropertyMetadata(default(ObservableCollection<AppBarButton>), UpdateCommands));

    private static void UpdateCommands(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
    {
        if (!(dependencyObject is BindableCommandBarBehavior behavior)) return;
        var oldList = dependencyPropertyChangedEventArgs.OldValue as ObservableCollection<AppBarButton>;
        if (dependencyPropertyChangedEventArgs.OldValue != null)
        {
            oldList.CollectionChanged -= behavior.PrimaryCommandsCollectionChanged;
        }

        var newList = dependencyPropertyChangedEventArgs.NewValue as ObservableCollection<AppBarButton>;
        if (dependencyPropertyChangedEventArgs.NewValue != null)
        {
            newList.CollectionChanged += behavior.PrimaryCommandsCollectionChanged;
        }
        behavior.UpdatePrimaryCommands();
    }


    private void PrimaryCommandsCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        UpdatePrimaryCommands();
    }


    private void UpdatePrimaryCommands()
    {
        if (PrimaryCommands != null)
        {
            AssociatedObject.PrimaryCommands.Clear();
            foreach (var command in PrimaryCommands)
            {
                AssociatedObject.PrimaryCommands.Add(command);
            }
        }
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        if (PrimaryCommands != null)
        {
            PrimaryCommands.CollectionChanged -= PrimaryCommandsCollectionChanged;
        }
    }
}

此行为实际上创建了一个可绑定的假PrimaryCommands属性,并且还可以观察集合更改的事件。每当发生更改时,都会重建命令。

最后,您的代码中的问题是您的AppBarButtonList只是一个字段,而不是一个属性。改变它:

public ObservableCollection<AppBarButton> AppBarButtonList { get; } = new ObservableCollection<AppBarButton> {
    new AppBarButton { Icon = new SymbolIcon(Symbol.Accept), Label="Bla" },
    new AppBarButton{Icon=new SymbolIcon(Symbol.Add),Label="Add"}
};

注意在赋值运算符之前添加的{get ;}

现在您可以像这样使用XAML中的行为:

<CommandBar>
    <interactivity:Interaction.Behaviors>
        <local:BindableCommandBarBehavior PrimaryCommands="{Binding Path=Content.AppBarButtonList, ElementName=rootFrame}" />
    </interactivity:Interaction.Behaviors>
</CommandBar>

这绝不是一个完美的解决方案,可以改进以允许不同的集合类型绑定等等,但它应该涵盖您的场景。另一种解决方案是实现命令栏的自定义版本,在类型上直接使用新的附加依赖项属性,但我使用行为使用户更清楚这是一个&#34;添加&#34;功能,而不是内置功能。<​​/ p>

答案 1 :(得分:0)

我发现此答案非常有帮助。我做了一些其他调整,例如使用DataTemplateSelector从可绑定数据源中删除UI引用,例如“ AppBarButton”。

import random

#
# Global variables
# Setup optimal string and GA input variables.
#

OPTIMAL     = 0                 # Ideal settle time is zero
DNA_SIZE    = 6                      # Amount of columns in parameter text file ??
POP_SIZE    = 20
GENERATIONS = 30

with open('../ResultingSettleTimes.txt') as f:
    items = f.readlines()
# you may also want to remove whitespace characters like `\n` at the end of each line
items = [x.strip('\n') for x in items]

#
# Helper functions
# These are used as support, but aren't direct GA-specific functions.
#
def random_Aff():
    """
    Return a random value of Aff between 0 and 6000.
    """
    return int(random.randrange(0, 6000, 1))

def random_Vff():
    """
    Return a random value of Vff between 0 and 40.
    """
    return float(40*random.uniform(0,1))

def random_RampRate():
    """
    Return a random value of Ramp Rate between 0 and 10000.
    """
    return int(random.randrange(0, 10000, 1))

def random_TrajFIRFilter():
    """
    Return a random value of Aff between 0 and 6000.
    """
    return int(random.randrange(0, 10, 1))

def random_FFA():
    """
    Return a random value of Feedforward Advance between 0 and 1.
    """
    return float(random.randrange(0, 8, 1)/8)

def random_population():
    """
    Return a list of POP_SIZE individuals, each randomly generated via iterating
    DNA_SIZE times to generate a string of random characters with random_char().
    """
    pop = []
    for i in xrange(POP_SIZE):
        #dna = ""
        dna_Aff = 0
        dna_Vff = 0
        dna_RampRate = 0
        dna_TrajFIRFilter = 0
        dna_FFA = 0
        for c in xrange(DNA_SIZE):
            #dna += random_char()
            #pop.append(dna)
            dna_Aff += random_Aff()
            dna_Vff += random_Vff()
            dna_FFA += random_FFA()
            dna_TrajFIRFilter += random_TrajFIRFilter()
            dna_RampRate += random_RampRate()
            pop.append(dna_Aff, dna_Vff. dna_FFA, dna_TrajFIRFilter, dna_RampRate)
    return pop

def weighted_choice(items):
    """
    Chooses a random element from items, where items is a list of tuples in
    the form  (GainAff, GainVff, FFA, TrajFIR, Ramp Rate, Settle Time).
    weight determines the probability of choosing its respective item.
    """
    weight_total = sum((item[5] for item in items)) #Weighting is Settle Time (6th column)
    n = random.uniform(0, weight_total)
    for item, weight in items:                  #This is if there are arrays with 1 thing and 1 weight - we have 5 things
        if n < weight:
            return item
        n = n - weight
    return item


#
# GA functions
# These make up the bulk of the actual GA algorithm.
#

def fitness(dna):
    """
    For each settle time in 'ResultingSettleTime.txt', this function calculates the difference between
    it and the OPTIMAL value, which is zero in this case. Here, LOWER fitness value is better.
    """
    fitness = 0
    fitness += items[5]
    return fitness

def mutate(dna):
    """
    For each gene in the DNA, there is a 1/mutation_chance chance that it will be
    switched out with a random value. This ensures diversity in the
    population, and ensures that is difficult to get stuck in local minima.
    """
    for i in range(0,len(items)):
        mutation_chance = 100
        if int(random.random()*mutation_chance) == 1:
            if i == 0:
                dna_Aff += random_Aff()
            if i == 1:
                dna_Vff += random_Vff()
            if i == 2:
                dna_RampRate += random_RampRate()
            if i == 3:
                dna_TrajFIRFilter += random_TrajFIRFilter()
            if i == 4:
                dna_FFA += random_FFA()      

#def crossover(dna1, dna2):
#  """
#  Slices both dna1 and dna2 into two parts at a random index within their
#  length and merges them. Both keep their initial sublist up to the crossover
#  index, but their ends are swapped.
#  """
#  pos = int(random.random()*DNA_SIZE)
#  return (dna1[:pos]+dna2[pos:], dna2[:pos]+dna1[pos:])

#
# Main driver
# Generate a population and simulate GENERATIONS generations.
#

if __name__ == "__main__":
  # Generate initial population. This will create a list of POP_SIZE strings,
  # each initialized to a sequence of random characters.
  population = random_population()

  # Simulate all of the generations.
  for generation in xrange(GENERATIONS):
    print "Generation %s... Random sample: '%s'" % (generation, population[0])
    weighted_population = []

    # Add individuals and their respective fitness levels to the weighted
    # population list. This will be used to pull out individuals via certain
    # probabilities during the selection phase. Then, reset the population list
    # so we can repopulate it after selection.
    for individual in population:
      fitness_val = fitness(individual)

      # Generate the (individual,fitness) pair, taking in account whether or
      # not we will accidently divide by zero.
      if fitness_val == 0:
        pair = (individual, 1.0)
      else:
        pair = (individual, 1.0/fitness_val)

      weighted_population.append(pair)

    population = []

    # Select two random individuals, based on their fitness probabilites, cross
    # their genes over at a random point, mutate them, and add them back to the
    # population for the next iteration.
    for _ in xrange(POP_SIZE/2):
      # Selection
      ind1 = weighted_choice(weighted_population)
      ind2 = weighted_choice(weighted_population)

      # Crossover
      #ind1, ind2 = crossover(ind1, ind2)

      # Mutate and add back into the population.
      population.append(mutate(ind1))
      population.append(mutate(ind2))

  # Display the highest-ranked string after all generations have been iterated
  # over. This will be the closest string to the OPTIMAL string, meaning it
  # will have the smallest fitness value. Finally, exit the program.
  #fittest_string = population[0]
  #minimum_fitness = fitness(population[0])

  #for individual in population:
  #  ind_fitness = fitness(individual)
  #  if ind_fitness <= minimum_fitness:
  #    fittest_string = individual
  #    minimum_fitness = ind_fitness

  #print "Fittest String: %s" % fittest_string
  exit(0)

DataTemplateSelector:

public class BindableCommandBarBehavior : Behavior<CommandBar>
{
    public static readonly DependencyProperty PrimaryCommandsProperty = DependencyProperty.Register(
        "PrimaryCommands", typeof(object), typeof(BindableCommandBarBehavior),
        new PropertyMetadata(null, UpdateCommands));

    public static readonly DependencyProperty ItemTemplateSelectorProperty = DependencyProperty.Register(
        "ItemTemplateSelector", typeof(DataTemplateSelector), typeof(BindableCommandBarBehavior),
        new PropertyMetadata(null, null));

    public DataTemplateSelector ItemTemplateSelector
    {
        get { return (DataTemplateSelector)GetValue(ItemTemplateSelectorProperty); }
        set { SetValue(ItemTemplateSelectorProperty, value); }
    }

    public object PrimaryCommands
    {
        get { return  GetValue(PrimaryCommandsProperty); }
        set { SetValue(PrimaryCommandsProperty, value); }
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        if (PrimaryCommands is INotifyCollectionChanged notifyCollectionChanged)
        {
            notifyCollectionChanged.CollectionChanged -= PrimaryCommandsCollectionChanged;
        }
    }

    private void UpdatePrimaryCommands()
    {
        if (AssociatedObject == null)
            return;

        if (PrimaryCommands == null)
            return;

        AssociatedObject.PrimaryCommands.Clear();

        if (!(PrimaryCommands is IEnumerable enumerable))
        {
            AssociatedObject.PrimaryCommands.Clear();
            return;
        }


        foreach (var command in enumerable)
        {
            var template = ItemTemplateSelector.SelectTemplate(command, AssociatedObject);

            if (!(template?.LoadContent() is FrameworkElement dependencyObject))
                continue;

            dependencyObject.DataContext = command;

            if (dependencyObject is ICommandBarElement icommandBarElement)
                AssociatedObject.PrimaryCommands.Add(icommandBarElement);
        }
    }

    protected override void OnAttached()
    {
        base.OnAttached();
        UpdatePrimaryCommands();
    }

    private void PrimaryCommandsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        UpdatePrimaryCommands();
    }

    private static void UpdateCommands(DependencyObject dependencyObject,
        DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
    {
        if (!(dependencyObject is BindableCommandBarBehavior behavior)) return;
        if (dependencyPropertyChangedEventArgs.OldValue is INotifyCollectionChanged oldList)
        {
            oldList.CollectionChanged -= behavior.PrimaryCommandsCollectionChanged;
        }

        if (dependencyPropertyChangedEventArgs.NewValue is INotifyCollectionChanged newList)
        {
            newList.CollectionChanged += behavior.PrimaryCommandsCollectionChanged;
        }

        behavior.UpdatePrimaryCommands();
    }
}

用于模板的Xaml:

public class CommandBarMenuItemTemplateSelector : DataTemplateSelector
{
    public DataTemplate CbMenuItemTemplate { get; set; }


    protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
    {
        if (item is ContextAction)
        {
            return CbMenuItemTemplate;
        }

        return base.SelectTemplateCore(item, container);
    }
}

用法:

 <DataTemplate x:Key="CbMenuItemTemplate">
    <AppBarButton
        Command="{Binding Command}"
        Icon="Add"
        Label="{Binding Text}" />
</DataTemplate>

<viewLogic:CommandBarMenuItemTemplateSelector x:Key="CommandBarMenuItemTemplateSelector" 
                                              CbMenuItemTemplate="{StaticResource CbMenuItemTemplate}" />

其中ContextActions是我的ContextAction类的ObservableCollection。

相关问题