Search code examples
c#xamldata-bindingwinui-3winui

Pass The Values of Multiple Controls Via A Button in WinUI 3


Using WinUi 3 I am trying to allow a user to add data to an SQLite database by the way of controls on a page. In this particular case I have 2 text boxes whose text value I send to a RelayCommand when a button is clicked.

My issue is it seems that only one CommandParamter can be set and so currently I can only send one of the textboxes' values. Is there a way to send the values of multiple controls at once?

View

<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
    <StackPanel Orientation="Horizontal">
        <TextBlock Text="First Name"/>
        <TextBox x:Name="firstNameBox" Header="First Name" />
    </StackPanel>
    <StackPanel Orientation="Horizontal">
        <TextBlock Text="Last Name"/>
        <TextBox x:Name="lastNameBox" Header="Last Name" />
    </StackPanel>
    <Button Content="New User" Margin="16" Command="{x:Bind ViewModel.AddParticipantCommand}" CommandParameter="{Binding Text, ElementName=firstNameBox}"/>
</StackPanel>

View Model

public partial class ParticipantViewModel: ObservableObject
{
    RecipeDBContext context;

    [ObservableProperty]
    private ObservableCollection<Participant> _participants;

    public ParticipantViewModel()
    {
        context = new RecipeDBContext();
        _participants = new ObservableCollection<Participant>();
        UpdateParticipants(context.Participants.ToList());
        _participants.CollectionChanged += this.OnCollectionChanged;
    }

    public void UpdateParticipants(List<Participant> participants)
    {
        Participants.Clear();        
        foreach (Participant participant in participants)
        {            
            Participants.Add(participant);
        }
    }

    [RelayCommand]
    public void AddParticipant(object o)
    {
        if (o != null)
        {
        }
    }

    void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {}

Model

public partial class Participant: ObservableObject
{
    RecipeDBContext context;
    public int Id { get; set; }

    [ObservableProperty]
    private string _firstName;

    [ObservableProperty]
    private string _lastName;                

    public Participant()
    {
        context = new RecipeDBContext();
    }
}

Solution

  • Let me show you one way to do this with a simple sample code using Function Bindings:

    public record PersonName(string FirstName, string LastName);
    
    public partial class SomeViewModel : ObservableObject
    {
        [ObservableProperty]
        private string _fullName = string.Empty;
    
        public static PersonName CreatePersonName(string firstName, string lastName) => new(firstName, lastName);
    
        [RelayCommand]
        private void Register(PersonName personName)
        {
            FullName = $"{personName.FirstName} {personName.LastName}";
        }
    }
    
    <TextBox
        x:Name="FirstNameTextBox"
        Text="John" />
    <TextBox
        x:Name="LastNameTextBox"
        Text="Doe" />
    <Button
        Command="{x:Bind ViewModel.RegisterCommand}"
        CommandParameter="{x:Bind local:SomeViewModel.CreatePersonName(FirstNameTextBox.Text, LastNameTextBox.Text), Mode=OneWay}"
        Content="Register" />
    <TextBlock Text="{x:Bind ViewModel.FullName, Mode=OneWay}" />