Click here to Skip to main content
15,885,365 members
Please Sign up or sign in to vote.
5.00/5 (2 votes)
See more:
I have following Scenario:

I have used one ItemsControl.
Which generates Button as per ItemsSource Given to it?

Now,

when i am hitting nextbutton.
The ItemsSource of ItemsControl(pageControl) Changes.[have a look mainwindow.xaml]
and also have to chagne the background of button which has the Content equals to CurrentPage property CurrentPage Property will Change(Increment to One) Each time i click nextButton

Suppose,
STEP 1: In click event of button first I will change ItemsSource of ItemsControl.
STEP 2: Then I will Change Background of particular button. But instead of changes in Background i am getting following Error.
This operation is valid only on elements that have this template applied.
NOTE:- I don’t have any problem if I directly changes Background without any changes in ItemsSource.[have a look inside mainwindow.xaml.cs]

Have a look at Below Code.
Mainwindow.xaml
XML
<Window x:Class="CurrentPageProblem.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <Style TargetType="Button" x:Key="buttonStyle">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="Button">
                        <Border  CornerRadius="2,2,2,2"  HorizontalAlignment="Center" x:Name="borderTemplate" Background="{TemplateBinding Background}">
                            <ContentPresenter/>
                        </Border>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsMouseOver" Value="true">
                                <Setter TargetName="borderTemplate"  Property="Border.BorderBrush" Value="Gray" />
                                <Setter TargetName="borderTemplate"  Property="Border.BorderThickness" Value="1" />
                            </Trigger>
                            <Trigger Property="IsPressed" Value="true">
                                <Setter TargetName="borderTemplate"  Property="Border.BorderBrush" Value="Lime" />
                            </Trigger>
                            <Trigger Property="IsFocused" Value="true">
                                <Setter TargetName="borderTemplate"  Property="Border.Background" Value="#FD7" />
                            </Trigger>
                            <Trigger Property="IsEnabled" Value="false">
                                <Setter TargetName="borderTemplate"  Property="Border.Background" Value="LightGray"></Setter>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="47*" />
            <RowDefinition Height="264*" />
        </Grid.RowDefinitions>
        <ItemsControl Name="pageControl" ItemsSource="{Binding Path=PageCollection}" Grid.Row="0">
            <ItemsControl.Template>
                <ControlTemplate TargetType="ItemsControl">
                    <Border >
                        <StackPanel>
                            <ItemsPresenter></ItemsPresenter>
                        </StackPanel>
                    </Border>
                </ControlTemplate>
            </ItemsControl.Template>
            <ItemsControl.ItemsPanel x:Uid="pageItemTemplate">
                <ItemsPanelTemplate>
                    <StackPanel Orientation="Horizontal"/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Button x:Name="pageNumberButton" Margin="3,4" Style="{StaticResource buttonStyle}" Content="{Binding Path=Page_Number}"></Button>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>

        <Button Content="Next" Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="136,98,0,0" Name="nextButton" VerticalAlignment="Top" Width="75" Click="button1_Click" />
    </Grid>
</Window>


Mainwindow.xaml.cs

public partial class MainWindow : Window,INotifyPropertyChanged
   {
       ObservableCollection<PageNumber> pageCollection = new ObservableCollection<PageNumber>();
       public MainWindow()
       {
           InitializeComponent();
           pageCollection.Add(new PageNumber("  0  "));
           pageCollection.Add(new PageNumber("  1  "));
           pageCollection.Add(new PageNumber("  2  "));
           pageCollection.Add(new PageNumber("  3  "));
           pageCollection.Add(new PageNumber("  4  "));
           pageCollection.Add(new PageNumber("  5  "));

           this.DataContext = this;
       }
       public ObservableCollection<PageNumber> PageCollection
       {
           get { return this.pageCollection; }
           set
           {
               this.pageCollection = value;
               this.OnPropertyChanged("PageCollection");
           }
       }

       private int currentPage;
       public int CurrentPage
       {
           get { return currentPage; }
           set
           {
               currentPage = value;
               this.OnPropertyChanged("CurrentPage");
           }
       }

       private void button1_Click(object sender, RoutedEventArgs e)
       {
           #region --  IF I COMMENT THIS MUCH CODE THEN THERE IS NO PROBLEM,,,PROBLEM OCCURES WHEN I UNCOMMENT THE CODE,,, --
           pageCollection.Clear();
           pageCollection.Add(new PageNumber("  0  "));
           pageCollection.Add(new PageNumber("  1  "));
           pageCollection.Add(new PageNumber("  2  "));
           #endregion
           for (int i = 0; i < pageControl.Items.Count; i++)
           {
               var container = pageControl.ItemContainerGenerator.ContainerFromIndex(i) as ContentPresenter;
               var button = container.ContentTemplate.FindName("pageNumberButton", container) as Button;
               if (button.Content.Equals(string.Format("  {0}  ", currentPage)))
               {
                   button.Background = Brushes.NavajoWhite;
               }
               else
               {
                   button.Background = nextButton.Background;
               }
           }
           currentPage++;
       }



       #region --  INotifyPropertyChanged Members  --
       public event PropertyChangedEventHandler PropertyChanged;
       public void OnPropertyChanged(string propertyNameArg)
       {
           PropertyChangedEventHandler handler = this.PropertyChanged;
           if (handler != null)
           {
               handler(this,new PropertyChangedEventArgs(propertyNameArg));
           }
       }
       #endregion
   }
   public class PageNumber
   {
       private string page_Number;
       public PageNumber(string pageNumberArg)
       {
           this.page_Number = pageNumberArg;
       }
       public string Page_Number
       {
           get { return page_Number; }
           set
           {
               page_Number = value;
           }
       }
   }


Thanks........
Posted
Updated 21-Jul-11 0:47am
v4

I think you should do it in XAML it self.
When we change the CurrentPage simply Notify it
and in xaml, use Multiple Trigger and Condition check the button content is same as CurrentPage then change the background.

Put the Multiple Trigger Style
 
Share this answer
 
Comments
Pritesh Aryan 21-Jul-11 8:47am    
where to put multiple trigger?? in style or in ItemControl???
Keith Barrow 21-Jul-11 10:10am    
Please have a look at my reply, it gives an example using a trigger.
Keith Barrow 21-Jul-11 10:10am    
Good answer!
WPF requires quite a shift of thinking from winforms, I prefer it. I suggest you look up the M-V-VM pattern for WPF. The first thing you need to do is separate yor View model, it makes the code much cleaner and separates concerns much better. Here is the ViewModel base class:

public abstract class ViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    public void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = this.PropertyChanged;
        if (handler != null)
            handler(this, new PropertyChangedEventArgs(propertyName));
    }
}


Then a view model for the item (please note the comments in code!):

public class PageNumber : ViewModel
{
    bool m_selected = false; //Don't differentiate between a field and its accessor just by the casing. Pre-pend with _ or m_ !
    string m_text;
    public string Text
    {
        get { return m_text; }
        set
        {
            if (m_text == value)
                return;
            m_text = value;
            OnPropertyChanged("Text");
        }
    }
    //This allows the binding to take care of how the item is displayed.
    public bool Selected
    {
        get { return m_selected; }
        set
        {
            if (m_selected == value)
                return;
            m_selected = value;
            OnPropertyChanged("Selected");
        }
    }
    public PageNumber(string pageNumberArg, bool selected = false)
    {
        //Don't access the field directly, unless needed!
        Text = pageNumberArg;
        Selected = selected;
    }
}

And now code to doing what was mixed up in the Window's class:
public class MyViewModel : ViewModel
    {
        PageNumber m_currentPage; //fields should not differ from their accessors by only the case use _name or m_name instead
        ObservableCollection<pagenumber> m_pageCollection = new ObservableCollection<pagenumber>();
        public PageNumber CurrentPage
        {
            get { return m_currentPage; }
            set
            {
                if (m_currentPage == value)
                    return; //prevent unecessary refreshing: potenitally expensive
                m_currentPage = value;
                OnPropertyChanged("CurrentPage");
            }
        }
        public ObservableCollection<pagenumber> PageCollection
        {
            get { return m_pageCollection; }
            set
            {
                if (m_pageCollection == value) 
                    return;
                m_pageCollection = value;
                OnPropertyChanged("PageCollection");
            }
        }
        public void Next()
        {
            //I got rid of the clear stuff you had etc for clarity.
            // Note this is far from perect!
            // 1) It is possible to select more than one item
            // 2) It would be better to have a single "SelectedPageNumber"
            //    property of type PageNumber in this MyView class than than 
            //    what I have here. This is just to demo the principle
            int currentIndex = PageCollection.IndexOf(PageCollection.First(x => x.Selected));
            PageCollection[currentIndex].Selected = false;
            PageCollection[currentIndex + 1].Selected = true; 
        }
        public MyViewModel()
        {
            PageCollection.Add(new PageNumber("  0  ", true));
            PageCollection.Add(new PageNumber("  1  "));
            PageCollection.Add(new PageNumber("  2  "));
            PageCollection.Add(new PageNumber("  3  "));
            PageCollection.Add(new PageNumber("  4  "));
            PageCollection.Add(new PageNumber("  5  "));
            CurrentPage = PageCollection[0];
        }
    }</pagenumber></pagenumber></pagenumber>


Now your window code becomes something simple:

public partial class MainWindow : Window
{
        public MyViewModel ViewModel
        {
            get { return DataContext as MyViewModel; }
            set { DataContext = value; }
        }
        public MainWindow()
        {
            InitializeComponent();
            ViewModel = new MyViewModel();
        }
        private void button1_Click(object sender, RoutedEventArgs e)
        {
            ViewModel.Next();
        }
}



Finally, your Items control XAML (Note the bold part)

<itemscontrol name="pageControl" itemssource="{Binding Path=PageCollection}" grid.row="0">
<itemscontrol.template>
    <controltemplate targettype="ItemsControl">
        <border>
            <stackpanel>
                <itemspresenter></itemspresenter>
            </stackpanel>
        </border>
    </controltemplate>
</itemscontrol.template>
<itemscontrol.itemspanel x:uid="pageItemTemplate" xmlns:x="#unknown">
    <itemspaneltemplate>
        <stackpanel orientation="Horizontal" />
    </itemspaneltemplate>
</itemscontrol.itemspanel>
<itemscontrol.itemtemplate>
    <datatemplate>
        <button x:name="pageNumberButton" margin="3,4" content="{Binding Path=Text}" xmlns:x="#unknown">
            <button.style>
                <style targettype="{x:Type Button}">
                    <setter property="Template">
                        <setter.value>
                            <controltemplate targettype="Button">
                                <border cornerradius="2,2,2,2" horizontalalignment="Center" x:name="borderTemplate" background="{TemplateBinding Background}">
                                    <contentpresenter />
                                </border>
                                <controltemplate.triggers>
                                    <trigger property="IsMouseOver" value="true">
                                        <setter targetname="borderTemplate" property="Border.BorderBrush" value="Gray" />
                                        <setter targetname="borderTemplate" property="Border.BorderThickness" value="1" />
                                    </trigger>
                                    <trigger property="IsPressed" value="true">
                                        <setter targetname="borderTemplate" property="Border.BorderBrush" value="Lime" />
                                    </trigger>
                                    <trigger property="IsFocused" value="true">
                                        <setter targetname="borderTemplate" property="Border.Background" value="#FD7" />
                                    </trigger>
                                    <trigger property="IsEnabled" value="false">
                                        <setter targetname="borderTemplate" property="Border.Background" value="LightGray"></setter>
                                    </trigger>
                                </controltemplate.triggers>
                            </controltemplate>
                        </setter.value>
                    </setter>
                    <style.triggers>
                        <datatrigger binding="{Binding Path=Selected}" value="true">
                            <setter property="Background" value="White" />
                        </datatrigger>
                    </style.triggers>
               </style>
            </button.style>
        </button>
    </datatemplate>
</itemscontrol.itemtemplate>


This uses a data trigger bound to the Selected property of the PageNumber view model, you could also use a converter to do a similar job. Additionally you can move the styling out to resources if you want to re-use it.

Notice that the there little direct UI stuff in the view model and that binding takes care of pretty much everything on the display side, this is normally the case if you get a WPF app right. I wrote a whole banking app with almost no code in the main window's class, and nearly everything was handled with templates and the M-V-VM pattern.
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

  Print Answers RSS
Top Experts
Last 24hrsThis month


CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900