简体   繁体   中英

Proper Code-Behind Object Data Binding in XAML?

I am currently binding an object in my code-behind (C#) to my XAML by giving a name to the XAML control, and setting the DataContext in the code-behind.

public partial class SmsControl: UserControl
{
    private readonly DataOrganizer _dataOrganizer;
    public FunctionalTester _funcTester;

    public SmsControl()
    {
        InitializeComponent();

        _dataOrganizer = new DataOrganizer();
        _funcTester = new FunctionalTester();

        // Set the datacontext appropriately
        grpModemInitialization.DataContext = _funcTester;
    }

    private async void Button_Click_2(object sender, RoutedEventArgs e)
    {
        await _funcTester.Test();
    }
}

And my XAML...

 <!-- SMS Test Modem Initialization GroupBox -->
    <GroupBox x:Name="grpModemInitialization" Grid.Column="1" Grid.Row="1" Grid.ColumnSpan="3"  Style="{StaticResource groupboxViewItem}">
        <GroupBox.Header>
            <Label Content="SMS Test Modem Initialization" Style="{StaticResource boldHeaderItem}"/>
        </GroupBox.Header>

        <!-- SMS Test Modem Initialization Grid -->
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="50"/>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>

            <Label Grid.Column="0" Grid.Row="0" Grid.RowSpan="2" Content="COM:" Style="{StaticResource boldHeaderItem}" />
            <ComboBox Grid.Column="1" Grid.Row="0" Grid.RowSpan="2" Style="{StaticResource comboBoxItem}" ItemsSource="{Binding AvailableCommPorts}" SelectedItem="{Binding SelectedCommPort}" />
            <Label Grid.Column="2" Grid.Row="0" Content="Modem Ready:" Style="{StaticResource boldHeaderItem}" />
            <Label Grid.Column="2" Grid.Row="1" Content="RSSI:" Style="{StaticResource boldHeaderItem}" />
            <Label Content="{Binding ModemReady}" Grid.Column="3" Grid.Row="0"  HorizontalContentAlignment="Left" VerticalAlignment="Center"/>
            <Label Content="{Binding ModemRssi}" Grid.Column="3" Grid.Row="1" HorizontalContentAlignment="Left" VerticalAlignment="Center" />
            <Label Grid.Column="4" Grid.Row="0" Content="Modem #:" Style="{StaticResource boldHeaderItem}"/>
            <Button Grid.Column="4" Grid.Row="1" Grid.ColumnSpan="2" Content="Initialize" />
            <Label Content="{Binding ModemNumber}" Grid.Column="5" Grid.Row="0" HorizontalContentAlignment="Left" VerticalAlignment="Center"/>
        </Grid>
    </GroupBox>

The code above works fine - no problems. What I'm asking is, if there is a way to set the DataContext of the GroupBox in XAML, referencing my _funcTester object, instead of setting the DataContext in the code-behind? The reason I ask, is because different controls need to be bound to different objects in the code-behind and I'm not finding good resources on how to do so, except as I show above (giving a "x:Name" to each XAML control and setting the DataContext in code-behind). Any help is appreciated! Thanks!

You don't want to reference UI elements by name in the code-behind. Actually any time you can avoid naming an object you save a little in performance . And by setting up your app to use MVVM properly, you gain in performance, readability, maintainability, and code separation.

You want to abstract things further to use the MVVM pattern. You're doing your bindings correctly but consider the pattern. Your view is all correct. Consider adding a class that holds the properties defined currently in your code-behind and the methods called in your event handlers.

public class ViewModel : INotifyPropertyChanged
{
    private FunctionalTester _funcTester;
    public FunctionalTester FuncTester
    {
        get
        {
            return _funcTester;
        }
        set
        {
            _funcTester = value;
            OnPropertyChanged( "FuncTester" );
        }
    }

    public async void TestAsync( )
    {
        await _funcTester.Test( );
    }
}

A binding to the FuncTester would simply be SomeProperty="{Binding FuncTester}" because the object is set as the DataContext of your view. A decent article that expands on this idea is found here .

Obviously left out some things (like INotifyPropertyChanged implementation and other properties you've defined) for brevity. But just make this class and assign it as your view model. Then the UI (the Xaml and the code-behind) only really deal with the UI and the ViewModel really deals with the data and logic. Great separation. For your event handler, just call ((ViewModel)this.DataContext).Test( ); and you can plug-and-play your ViewModel's to change functionality on the fly. Hope this helps!

Just set the DataContext of whole UserControl to self ie do

this.DataContext = this; in constructor.

Then define the Property for _functinTester

public FunctionalTester FuncTester { get {return _funcTester} };

Now in your xaml you can do

<GroupBox x:Name="grpModemInitialization" DataContext="{Binding FuncTester}"/>

In this way since you have the DataContext set for your whole usercontrol, you can bind any control to any property within that DataContext

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