简体   繁体   中英

Binding string items to a dependency property list of strings in xaml

I have a list of strings defined as a dependency property. I am assigning the values of the list's items via xaml. I can assign literal string values, but I do not know how to bind those values to data from viewmodel.

public static readonly DependencyProperty StringArgsProperty = DependencyProperty.Register(
        "StringArgs", typeof(List<string>), typeof(GameTextBlock), new FrameworkPropertyMetadata(new List<string>()));

    public List<string> StringArgs
    {
        get { return (List<string>)GetValue(StringArgsProperty); }
        set { SetValue(StringArgsProperty, value); }
    }

Here's how I can bind items to said list currently:

<common:GameTextBlock.StringArgs>
  <sys:String>arg1</sys:String>
  <sys:String>arg2</sys:String>
</common:GameTextBlock.StringArgs>

What I am trying to do is replace arg1/arg2 with values from ViewModel. If I wasn't assigning to the list's items I could do "{Binding NameOfField}".

I do not want to create the whole list in ViewModel and bind the list, because I want to be able to pick and choose the items using only xaml. Can this be achieved?

EDIT: A more clear example of what I want to achieve:

public class ViewModel
{
   public string Item1 {get; set;}
   public string Item2 {get; set;}
   public string Item3 {get; set;}
}

And then I want to be able to use them in one xaml containing multiple GameTextBlocks like this:

<GameTextBlock x:Name = "txt1" > 
<GameTextBlock.StringArgs>
  <sys:String>{Binding VMItem1}</sys:String>
  <sys:String>{Binding VMItem3}</sys:String>
</GameTextBlock.StringArgs>
</GameTextBlock>

<GameTextBlock x:Name = "txt2" > 
<GameTextBlock.StringArgs>
  <sys:String>{Binding VMItem1}</sys:String>
  <sys:String>{Binding VMItem2}</sys:String>
</GameTextBlock.StringArgs>
</GameTextBlock>

<GameTextBlock x:Name = "txt3" > 
<GameTextBlock.StringArgs>
  <sys:String>{Binding VMItem1}</sys:String>
</GameTextBlock.StringArgs>
</GameTextBlock>

Static data on a xaml page cannot change. Move the list structures to the code behind and on the change handler of the dependency property move the data as needed to the property lists. Regardless, this is how it should be done.

If you need two way transfer, then you will need more code behind to move the data from the control lists on the page back to the storage; still, this can be used as a template.

Control Code behind

Data Lists

Here are the lists on the custom control to be split out to:

public partial class ShowDependency : UserControl, INotifyPropertyChanged
{
 
private List<string> _Text1;

public List<string> Text1
{
    get { return _Text1; }
    set { _Text1 = value; OnPropertyChanged(nameof(Text1)); }
}

private List<string> _Text2;

public List<string> Text2
{
    get { return _Text2; }
    set { _Text2 = value; OnPropertyChanged(nameof(Text2)); }
}
Dependency property with PropertyChanged Handler

Here is the dependency property which will hold the incoming list from the VM

#region public List<string> Storage
/// <summary>Storage is a transfer from the Main VM</summary>
public List<string> Storage
{
    get => (List<string>)GetValue(StorageProperty);
    set => SetValue(StorageProperty, value);
}

/// <summary>Identifies the Storage dependency property.</summary>
public static readonly DependencyProperty StorageProperty =
    DependencyProperty.Register(
        "Storage",
        typeof(List<string>),
        typeof(ShowDependency),
        new PropertyMetadata(null, OnStoragePropertyChanged));
Change Handler

Once bound, it smurfs the data into the lists.

/// <summary>StorageProperty property changed handler.</summary>
/// <param name="d">ShowDependency that changed its Storage.</param>
/// <param name="e">Event arguments.</param>
private static void OnStoragePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    if (d is ShowDependency source)
    {
        var value = (List<string>)e.NewValue;

        if (value != null)
        {
            var l1 = new List<string>();
            var l2 = new List<string>();
            int count = 1;
            foreach (var item in value)
                if ((count++ % 2) == 0)
                    l1.Add(item);
                else 
                    l2.Add(item);

            source.Text1 = l1;
            source.Text2 = l2;
        }

    }
}
#endregion public List<string> Storage
Binding List of Strings on Main VM
 new List<string>() { "Alpha", "Baker", "Tango", "Omega" }
Custom Control Page
<UserControl x:Class="ExamplesInWPF.Controls.ShowDependency"
             ...
             d:DesignHeight="450" d:DesignWidth="800"
             x:Name="MyUserControl">
    <StackPanel Orientation="Vertical">
        <ListBox Margin="6" ItemsSource="{Binding Text1, ElementName=MyUserControl}"/>
        <ListBox Margin="6" ItemsSource="{Binding Text2, ElementName=MyUserControl}"/>        
    </StackPanel>
</UserControl>
Result

在此处输入图像描述


As an aside I have always used these Control dependency property snippets

Helpful Silverlight Snippets

Disregard the Silverlight part, they are still good and work up to.Net 6 Xaml/WPF.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM