简体   繁体   中英

Accessing a control within a control template in C#

WPF newbie here. I am trying to build a table that I would like to reuse for each day of the week. So I moved it to a control template. Here is the XAML:

<ControlTemplate x:Key="defaultGridDesign" >
            <Grid HorizontalAlignment="Center" Margin="0,5">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="80"/>
                    <ColumnDefinition Width="80"/>
                    <ColumnDefinition Width="200"/>
                    <ColumnDefinition Width="60"/>
                    <ColumnDefinition Width="60"/>
                    <ColumnDefinition Width="60"/>
                    <ColumnDefinition Width="60"/>
                    <ColumnDefinition Width="60"/>
                    <ColumnDefinition Width="60"/>
                    <ColumnDefinition Width="60"/>
                    <ColumnDefinition Width="60"/>
                    <ColumnDefinition Width="200"/>
                    <ColumnDefinition Width="200"/>
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition Height="30"/>
                    <RowDefinition Height="60"/>
                    <RowDefinition Height="60"/>
                </Grid.RowDefinitions>

                <Label x:Name="lblDayOfWeek" Style="{StaticResource BasicLabelLeft}" Grid.Row="0" Grid.Column="0" Grid.RowSpan="3" Content="Monday"/>
                <Label x:Name="lblEmpty" Style="{StaticResource BasicLabelLeft}" Grid.Row="0" Grid.Column="1"/>
                <Label x:Name="lblPlanned" Style="{StaticResource BasicLabelLeft}" Content="Planned" Grid.Row="1" Grid.Column="1"/>
                <Label x:Name="lblProduced" Style="{StaticResource BasicLabelLeft}"  Content="Produced" Grid.Row="2" Grid.Column="1"/>
                <Label x:Name="lblPrevShift" Style="{StaticResource BasicLabelCenter}" Content="Previous Shift" Grid.Row="0" Grid.Column="2" />
                <Label x:Name="lblPlannedPrevShift" Style="{StaticResource BasicLabelLeft}" Content="n/a" Grid.Row="1" Grid.Column="2" />
                <Label x:Name="lblProducedPrevShift" Style="{StaticResource BasicLabelLeft}"  Content="n/a" Grid.Row="2" Grid.Column="2" />
                <Label x:Name="lbl1stHr" Style="{StaticResource BasicLabelCenter}" Content="01" Grid.Row="0" Grid.Column="3" />
                <Label x:Name="lbl2ndHr" Style="{StaticResource BasicLabelCenter}" Content="02" Grid.Row="0" Grid.Column="4" />
                <Label x:Name="lbl3rdHr" Style="{StaticResource BasicLabelCenter}" Content="03" Grid.Row="0" Grid.Column="5" />
                <Label x:Name="lbl4thHr" Style="{StaticResource BasicLabelCenter}" Content="04" Grid.Row="0" Grid.Column="6" />
                <Label x:Name="lbl5thHr" Style="{StaticResource BasicLabelCenter}" Content="05" Grid.Row="0" Grid.Column="7" />
                <Label x:Name="lbl6thHr" Style="{StaticResource BasicLabelCenter}" Content="06" Grid.Row="0" Grid.Column="8" />
                <Label x:Name="lbl7thHr" Style="{StaticResource BasicLabelCenter}" Content="07" Grid.Row="0" Grid.Column="9" />
                <Label x:Name="lbl8thHr" Style="{StaticResource BasicLabelCenter}" Content="08" Grid.Row="0" Grid.Column="10"/>
                <Label x:Name="lblTotal" Style="{StaticResource BasicLabelCenter}" Content="Total" Grid.Row="0" Grid.Column="11"/>
                <Label x:Name="lblNextShift" Style="{StaticResource BasicLabelCenter}" Content="Next Shift" Grid.Row="0" Grid.Column="12"/>
                <Label x:Name="lblPlannedTotal" Style="{StaticResource BasicLabelLeft}" Content="n/a" Grid.Row="1" Grid.Column="11"/>
                <Label x:Name="lblPlannedNextShift" Style="{StaticResource BasicLabelLeft}" Content="n/a" Grid.Row="1" Grid.Column="12"/>
                <Label x:Name="lblProducedTotal" Style="{StaticResource BasicLabelLeft}" Content="n/a" Grid.Row="2" Grid.Column="11"/>
                <Label x:Name="lblShiftOEE" Style="{StaticResource BasicLabelLeft}" Content="n/a" Grid.Row="2" Grid.Column="12"/>
                <StackPanel x:Name="stkPnlPlanned" Grid.Row="1" Grid.Column="3" Grid.ColumnSpan="8" Width="480"/>
                <StackPanel x:Name="stkPnlProduced" Grid.Row="2" Grid.Column="3" Grid.ColumnSpan="8" Width="480"/>
            </Grid>
        </ControlTemplate>

This is how I instantiate them:

<StackPanel>
        <ContentControl Template="{StaticResource defaultGridDesign}" x:Name="ccTuesday"></ContentControl>
        <ContentControl Template="{StaticResource defaultGridDesign}" x:Name="ccWednesday" />
        <ContentControl Template="{StaticResource defaultGridDesign}" x:Name="ccThursday" />
        <ContentControl Template="{StaticResource defaultGridDesign}" x:Name="ccFriday" />
        <ContentControl Template="{StaticResource defaultGridDesign}" x:Name="ccSaturday" />
        <ContentControl Template="{StaticResource defaultGridDesign}" x:Name="ccMonday" />
    </StackPanel>

Now where I hit a brick wall, is when I want to bind the labels inside the controltemplate. Every example for binding I've found on the net uses a basic example, and I haven't managed to find one that goes over this kind of situation. I basically want to set the hours in the top row and the day in the first column, but eventually I need to fill the stack panels with labels depending on what I read from an excel file. And I expect the binding/filling of the stack panels to be a whole other ballpark. Each of the tables gets different information. Each hour, I need to update each of them.

I've tried this to no avail:

Label test = (Label) this.ccTuesday.Template.FindName("lblDayOfWeek", ccTuesday);

But the result is null.

And I have been looking at this discussion over here: https://codereview.stackexchange.com/questions/44760/is-there-a-better-way-to-get-a-child

But it really seems as a lot of overhead in order to access one label.

Again, I am a newbie, so I don't know if I am doing it correctly. Perhaps there is a better and more elegant way to do this. Any suggestions are welcome.

EDIT 1:
The answer below works, however when I try to implement a viewModel class between the View and the Model, as in this link http://www.codeproject.com/Articles/819294/WPF-MVVM-step-by-step-Basics-to-Advance-Level#Level3:-Addingactionsand “INotifyPropertyChanged”interface

the labels go back to being empty.

The View class:

public partial class MainWindow : Window
    {
        private TableViewModel tableViewModelMonday = null;
        private TableViewModel tableViewModelTuesday = null;

        public MainWindow()
        {
            InitializeComponent();
            try
            {
                this.tableViewModelMonday = new TableViewModel("Monday");
                this.tableViewModelTuesday = new TableViewModel("Tuesday");
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private void ccTuesday_Loaded(object sender, RoutedEventArgs e)
        {
            this.ccTuesday.DataContext = this.tableViewModelTuesday;
        }

        private void ccMonday_Loaded(object sender, RoutedEventArgs e)
        {
            this.ccMonday.DataContext = this.tableViewModelMonday;
        }

The app.xaml where the binding is:

<Label x:Name="lblDayOfWeek" Style="{StaticResource BasicLabelLeft}" Grid.Row="0" Grid.Column="0" Grid.RowSpan="3" Content="{Binding dayOfWeek}"/>

The ViewModel class:

public class TableViewModel : INotifyPropertyChanged
    {
        private TableModel tableModel = null;

        public TableViewModel(string day)
        {
            tableModel = new TableModel(day);
        }

        public string lblDayOfWeek
        {
            get
            {
                return tableModel.dayOfWeek;
            }
            set
            {
                if (tableModel.dayOfWeek != value)
                {
                    tableModel.dayOfWeek = value;
                    this.OnNotifyPropertyChanged("dayOfWeek");
                }
            }
        }

        private void OnNotifyPropertyChanged(string msg)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(msg));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

    }

And the model class:

public class TableModel
    {
        private string _dayOfWeek;

        public string dayOfWeek
        {
            get
            {
                return this._dayOfWeek;
            }
            set
            {
                if (this._dayOfWeek != value)
                {
                    this._dayOfWeek = value;
                }
            }
        }

        public TableModel(string day)
        {
            this._dayOfWeek = day;
        }
    }

What am I missing?

You can achieve that reusability by a UserControl too. In either ways, to get the binding functionality, you can define a Model type and assign it to DataContext property of the ContentControl (or UserControl).

First, bind the label inside the ContentTemplate:

<Label x:Name="lblDayOfWeek"
       Style="{StaticResource BasicLabelLeft}"
       Grid.Row="0"
       Grid.Column="0"
       Grid.RowSpan="3"
       Content="{Binding DayOfWeek}"/>

Then define model class:

public class Model : INotifyPropertyChanged {
    private DayOfWeek dayOfWeek;
    public DayOfWeek DayOfWeek {
        get {
            return this.dayOfWeek;
        }
        set {
            if (this.dayOfWeek != value) {
                this.dayOfWeek = value;
                this.OnNotifyPropertyChanged("DayOfWeek");
            }
        }
    }
    private void OnNotifyPropertyChanged(string name) {
        if (this.PropertyChanged != null) {
            this.PropertyChanged(this, new PropertyChangedEventArgs(name));
        }
    }
    public event PropertyChangedEventHandler PropertyChanged;
}

Then you need to create an instance of the Model and assign it to the DataContext:

public partial class MainWindow : Window {
    private ModelClass model = new ModelClass();
    public MainWindow() {
        InitializeComponent();
        this.customUserControl.DataContext = this.model;
    }

    private void button_Click(object sender, RoutedEventArgs e) {
        this.model.DayOfWeek = DayOfWeek.Sunday;
    }
}

Now, whenever you change DayOfWeek property of the model object, bound label in the ContentTemplate gets updated.

And about adding items to the StackPanel dynamically, you can consider using the ItemsControl class.

Just to let you know, the MVVM pattern is the most common design pattern used to design WPF applications. Following this pattern allow you to use powerful WPF features like binding better.

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