WPF ItemsControl:-在运行时更改ItemsControl的ItemSource属性后,更改ItemControle中的项目属性 [英] WPF ItemsControl:- Changes Property of Items inside ItemControle after ItemSource Property of ItemsControl being changed at Run-Time

查看:113
本文介绍了WPF ItemsControl:-在运行时更改ItemsControl的ItemSource属性后,更改ItemControle中的项目属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下情况:

我用了一个ItemsControl.
哪个会根据ItemsSource 生成Button ?

现在

当我点击nextbutton时.
ItemsControl(pageControl)ItemsSource 更改. [看一下mainwindow.xaml]
并且还必须更改按钮的背景,该按钮的Content等于CurrentPage 属性CurrentPage属性将每次更改(递增为1),每次单击nextButton

假设,
步骤1:首先在按钮的单击事件中,我将更改ItemsControl的ItemsSource.
步骤2:然后,我将更改特定按钮的背景.但是我没有跟随Background的变化,而是跟随Error.
This operation is valid only on elements that have this template applied.
注意:- 如果我直接更改Background而没有对ItemsSource进行任何更改,我不会有任何问题. [在 mainwindow.xaml.cs内部查看]

看看下面的代码.
Mainwindow.xaml

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

<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



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........

推荐答案

我认为您应该自己在XAML中完成它.
当我们更改CurrentPage时,只需通知它
在xaml中,使用Multiple Trigger和Condition检查按钮内容是否与CurrentPage相同,然后更改背景.

放置多重触发样式
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


WPF需要从Winforms转变思维,我更喜欢.我建议您查找WPF的M-V-VM模式.您需要做的第一件事是单独的View模型,它使代码很多更清晰,并且将关注点更好地分离了.这是ViewModel基类:

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;
    }
}


现在编写代码来完成Window类中的混合操作:


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();
        }
}




最后,您的Items控件XAML(请注意粗体部分)

< itemscontrol name ="pageControl" itemssource ="{Binding Path = PageCollection}" grid.row ="0">




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>



这使用绑定到PageNumber视图模型的Selected属性的数据触发器,您也可以使用转换器来完成类似的工作.此外,如果要重复使用,可以将样式移到资源上.

请注意,视图模型中几乎没有直接的UI内容,并且绑定可以处理显示端的几乎所有内容,如果您正确使用WPF应用程序,通常就是这种情况.我写了一个完整的银行应用程序,在主窗口的类中几乎没有任何代码,几乎所有内容都由模板和M-V-VM模式处理.



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.


这篇关于WPF ItemsControl:-在运行时更改ItemsControl的ItemSource属性后,更改ItemControle中的项目属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆