When I create ListBox with virtualization enabled and then update all its items appearance it works very fast. But when i slowly scroll down all items in ListBox and then update all items appearance it takes a lot of time. I think it because VirtualizingStackPanel does not destroy items when they are runs out of viewport.
I wrote simple app to reproduce this behavior.
Code:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
for(int i = 0; i < 5000; ++i) // creating 5k text boxes
MyList.Items.Add(new TextBox() { Text = CurrText });
}
private void Button_Click(object sender, RoutedEventArgs e)
{
GC.Collect();
n = (n + 1) % 2; // switch 0 to 1 or 1 to 0
foreach (var item in MyList.Items)
((TextBox)item).Text = CurrText; // set new text
}
static int n = 0;
string CurrText { get { return new string(n.ToString()[0], 50); } }
}
XAML:
<Window x:Class="VPanel.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="700" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="5*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ListBox Name="MyList" VirtualizingStackPanel.IsVirtualizing="True"/>
<Button Grid.Row="1" Content="UpdateText" Click="Button_Click"/>
</Grid>
</Window>
Clicking a button "UpdateText" updates all textboxes text. If slowly scroll to end by dragging scroller, "UpdateText" button clicks with a huge lag.
Don't create TextBoxes manually.
The word "virtualize" refers to a technique by which a subset of user interface (UI) elements are generated from a larger number of data items based on which items are visible on-screen. Generating many UI elements when only a few elements might be on the screen can adversely affect the performance of your application.
You create your UI items manually so it's already too late for virtualization. Use bindings and it will create TextBox from ItemTemplate whenever it is required. It will also not refresh TextBox.Text value if it's not currently in the view. To do that change your MainWindow to create ObservableCollection instead of TextBoxes and operate on that:
public partial class MainWindow : Window
{
private readonly ObservableCollection<string> _textBoxes = new ObservableCollection<string>();
public ICollection<string> TextBoxes { get { return _textBoxes; } }
private int n = 0;
private string CurrText { get { return new string(n.ToString()[0], 50); } }
public MainWindow()
{
for (int i = 0; i < 5000; ++i) _textBoxes.Add(CurrText);
InitializeComponent();
DataContext = this;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
n = (n + 1) % 2; // switch 0 to 1 or 1 to 0
for (int i = 0; i < _textBoxes.Count; i++) _textBoxes[i] = CurrText;
}
}
and then change XAML to bind to TextBoxes list property
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="5*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ListBox ItemsSource="{Binding TextBoxes}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=., Mode=TwoWay}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Grid.Row="1" Content="UpdateText" Click="Button_Click"/>
</Grid>
Related
I'm currently creating a board game in WPF. I'm creating my board in my PlayerViewModel so I have a nice 10x10 board. I also create pieces that the player starts with (4). Here is where I get stuck, because I'm not quite sure if it's possible to just put the player's pieces in my GamePiece.xaml the way I've done it since this is the "component" that creates the whole board for me. The circle/image is the component I want as a player's disc.
I bind this into my GameView.xaml, however the problem is that I want to have circles as my pieces in the game. Obviously here I'm just creating the whole board and so it's also creating the pieces (circles/photos) and I can't seem to manipulate this and decide how many I want to show on the board in the beginning. I have tried different ways, like put the pieces on specific coordinates and just having the color of the square it's taking up on the board change, but it doesn't look very nice.
First, you would create a ListView and configure it to place the items in rows (by using WrapPanel as ItemsPanel and setting the width and height for the ListView), and -as you mentioned- you can use BoardPiece UserControl as ItemTemplate.
Your GameView.xaml can be something similar to this
<UserControl
x:Class="GameView">
<StackPanel Orientation="Vertical">
<ListView
Width="600"
Height="500"
ItemsSource="{Binding BoardPieces}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<testingThings:BoardPiece Margin="0" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button
Width="100"
Margin="8"
Click="ButtonBase_OnClick"
Content="Fill All" />
</StackPanel>
</UserControl>
Then, Configure the ViewModel: create your items and store them in an ObservableCollection to bind them later with the ListView..
public class GameViewViewModel
{
public ObservableCollection<BoardPieceItem> BoardPieces { set; get; } =
new ObservableCollection<BoardPieceItem>();
public GameViewViewModel()
{
for (var i = 0; i < 10; i++)
for (var j = 0; j < 10; j++)
{
// random coloring at initialization, do it as you want..
BoardPieces.Add(new BoardPieceItem
{
Index = (i, j),
RectangleColor = "#00FF00", // green
EllipseColor = (i + j) % 2 == 0
? "#00FFFFFF" // transparent
: "#000000", // Black
});
}
}
}
Now, Bind the ViewModel with the View
public partial class GameView
{
public GameView()
{
InitializeComponent();
DataContext = new GameViewViewModel();
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
if (sender is Button { DataContext: TestDialogVm vm })
{
foreach (var item in vm.BoardPieces)
{
item.EllipseColor = "#0000FF";
}
}
}
}
In BoardPiece.xaml there is no need to use Converters
<UserControl
x:Class="SharedModule.Views.TestLab.BoardPiece"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:testLab="clr-namespace:SharedModule.Views.TestLab"
d:DataContext="{d:DesignInstance testLab:BoardPieceItem}"
MouseDown="UIElement_OnMouseDown"
mc:Ignorable="d">
<Grid>
<Rectangle
Width="45"
Height="45"
Fill="{Binding RectangleColor}"
Stroke="Black"
StrokeThickness="1.3" />
<Ellipse
Width="20"
Height="20"
Fill="{Binding EllipseColor}" />
</Grid>
</UserControl>
Finally, Configure the BoardPieceItem to make it possible to update the game board at runtime..
public class BoardPieceItem : INotifyPropertyChanged
{
public (int, int) Index { get; set; }
private string _rectangleColor;
public string RectangleColor
{
get => _rectangleColor;
set
{
_rectangleColor = value;
OnPropertyChanged();
}
}
private string _ellipseColor;
public string EllipseColor
{
get => _ellipseColor;
set
{
_ellipseColor = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Now you are all set, see how UIElement_OnMouseDown will update the item's color, and ButtonBase_OnClick will update the cells of the board at all (I could go all the way MVVM, but used some events in a hurry).
This is how the game looks like at my side
I have a liveChart and am creating checkboxes for each item in a list. This list also has data for each series in liveCharts. How do I bind my dynamically created checkboxes with each individual LiveCharts.LineSeries from my data?
I've created the checkboxes:
<!-- Creating checkboxes by binding to list -->
<ListView ItemsSource="{Binding ElementItemList}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled" Width="600">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=ElementName}" />
<CheckBox IsChecked="{Binding Path=ElementIsSelected}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListView>
<!-- Display the chart -->
<Grid Grid.Row="1" x:Name="TestGrid"></Grid>
So I assume that you want to have a CheckBox representing each LineSeries in your SeriesCollection.
So I would have two public properties, one for the SeriesCollection and the other for the list of CheckBox controls.
public SeriesCollection SeriesCollection { get; set; }
public List<CheckBox> CheckBoxCollection { get; set; }
Then following is a function that mimics dynamically creating your LineSeries and CheckBox controls since you didn't provide that code. It is important to have some sort of a connection between the CheckBox controls and your line series, and in this case I decided to set LineSeries.Title and CheckBox.Name the same.
Also note that in order to have the CheckBox do something upon checking/unchecking, you'd need to register two events for each.
public void DynamicallyCreateStuff()
{
SeriesCollection = new SeriesCollection();
CheckBoxCollection = new List<CheckBox>();
var count = 3;
var val1 = new List<double>() { 1, 2, 3 };
var val2 = new List<double>() { 9, 5, 3 };
var val3 = new List<double>() { 1, 4, 9 };
for (int i = 1; i <= count; i++)
{
var name = string.Format("LineSeries{0}", i);
var checkBox = new CheckBox
{
Name = name,
Content = name,
Margin = new Thickness() { Left = 8, Top = 8, Right = 8, Bottom = 8 },
IsChecked = true
};
checkBox.Checked += DynamicCheckBoxChecked;
checkBox.Unchecked += DynamicCheckBoxUnchecked;
CheckBoxCollection.Add(checkBox);
var lineSeries = new LineSeries
{
Title = name
};
if (i == 1)
{
lineSeries.Values = new ChartValues<double>(val1);
}
else if (i == 2)
{
lineSeries.Values = new ChartValues<double>(val2);
}
else if (i == 3)
{
lineSeries.Values = new ChartValues<double>(val3);
}
SeriesCollection.Add(lineSeries);
}
}
In my case, I decided to have the corresponding series become visible/hidden upon clicking the CheckBox, so my check/uncheck methods look like this:
private void DynamicCheckBoxChecked(object sender, EventArgs e)
{
ShowHideSeries(sender, Visibility.Visible);
}
private void DynamicCheckBoxUnchecked(object sender, EventArgs e)
{
ShowHideSeries(sender, Visibility.Collapsed);
}
private void ShowHideSeries(object sender, Visibility visibility)
{
var checkBox = (CheckBox)sender;
var found = SeriesCollection.FirstOrDefault(x => x.Title == checkBox.Name);
if (found != null)
{
var series = (LineSeries)found;
series.Visibility = visibility;
}
}
I didn't use a ViewModel in order to save time and for the sake of simplicity, so my MainWindow constructor looks like this:
public MainWindow()
{
InitializeComponent();
DynamicallyCreateStuff();
DataContext = this;
}
And XAML is pretty bare bones here:
<Window x:Class="SOLineCharts.MainWindow"
....
xmlns:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf"
mc:Ignorable="d"
WindowStartupLocation="CenterScreen"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ItemsControl Grid.Column="0"
ItemsSource="{Binding CheckBoxCollection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Vertical" ScrollViewer.HorizontalScrollBarVisibility="Disabled"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<lvc:CartesianChart Series="{Binding SeriesCollection}" Grid.Column="1"/>
</Grid>
</Window>
Result:
Upon loading:
Unchecking one check box:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Grid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Button button1 = new Button();
Point tPosition = Mouse.GetPosition(this);
button1.Margin = new Thickness(tPosition.X,tPosition.Y,0,0) ;
button1.Width = 75;
this.AddChild(button1);
}
}
I think the code is pretty self-explanatory, I used a code that is approved in other thread here in stackoverflow, it doesn't give any error, but it also doesn't show up, do I need to refresh the window? And how?
by request, the XAML
<Window x:Class="Ampeldingensthingy.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">
<Grid MouseLeftButtonDown="Grid_MouseLeftButtonDown" Name="hans">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="398*" />
<ColumnDefinition Width="105*" />
</Grid.ColumnDefinitions>
</Grid>
</Window>
Window can have only one children and I think you already have one that is Grid whose MouseButtonDown you subscribing. Give a name to that Grid and then replace
this.AddChild(button1);
with
grid.Children.Add(button1);
here grid is the name given to Grid.
The object "this" depicts the Window object here. Content of a ContentControl must be a single element. Hence append the Button that is "button1" inside a parent Grid. For example
private void Button_Click(object sender, RoutedEventArgs e)
{
Button button1 = new Button();
Point tPosition = Mouse.GetPosition(this);
button1.Margin = new Thickness(tPosition.X, tPosition.Y, 0, 0);
button1.Width = 75;
MainGrid.Children.Add(button1);
}
And in your Xaml
<Grid x:Name="MainGrid">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Button Click="Button_Click" Content="click" Grid.Row="1"/>
</Grid>
public partial class MainWindow : Window
{
public StackPanel SPanel{get;set;}
public MainWindow()
{
InitializeComponent();
SPanel = new StackPanel { Orientation = Orientation.Vertical };
}
private void Grid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Button button1 = new Button();
Point tPosition = Mouse.GetPosition(this);
button1.Margin = new Thickness(tPosition.X,tPosition.Y,0,0) ;
button1.Width = 75;
SPanel.Children.Add(button1);
window.Content = SPanel;
}
}
try to do the same with a canvas. the buttons will popup at the position you gave them.
I'm organizing my grid with RowDefinitions and ColumnDefinition, but forever when I want add a new RowDefinition in before actual any RowDefinition, I need reorganize Grid.Row of all controls
I saw RowDefinition and ColumnDefinition has a Name property, so I think is possible define Grid.Row with RowDefinition name or not? If is possible, How do
<Grid>
<Grid.RowDefinitions>
<RowDefinition Name="RowDictTitle" Height="27"/>
<RowDefinition Name="RowSearchWord" Height="27"/>
<RowDefinition/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<!--Row 1-->
<TextBlock Text="Word:" VerticalAlignment="Center" Margin="10,0,0,0" Grid.Row="1"/>
<TextBox Name="Search" Grid.ColumnSpan="2" Margin="50,2,10,2"/>
<!--Row 2-->
<ListBox Name="Words" Grid.Row="2" Margin="10"/>
</Grid>
I want make below
<TextBlock Text="Word" Grid.Row="RowSearchWord"/>
Disclaimer: This answer is kind of a self-advertisement within the constraints alluded to by this meta post. It advertises a free open source project that I (at the time of writing this) do not earn any money with. The only gain is the knowledge that my time for writing the described control was not wasted if it helps some future visitors of this SO question.
I had exactly the same thoughts. That is why, not too long ago, I wrote a custom grid class that uses named columns and rows.
I put it on Codeplex under the MIT license: Name-Based Grid project
With that control, you can rewrite your Xaml source code as follows:
<nbg:NameBasedGrid>
<nbg:NameBasedGrid.RowDefinitions>
<nbg:ColumnOrRow Name="RowDictTitle" Height="27"/>
<nbg:ColumnOrRow Name="RowSearchWord" Height="27"/>
<nbg:ColumnOrRow Name="List"/>
<nbg:ColumnOrRow Height="50"/>
</nbg:NameBasedGrid.RowDefinitions>
<nbg:NameBasedGrid.ColumnDefinitions>
<nbg:ColumnOrRow Width="1*" Name="Left"/>
<nbg:ColumnOrRow Width="2*" Name="Right"/>
</nbg:NameBasedGrid.ColumnDefinitions>
<!--Row 1-->
<TextBlock Text="Word:" VerticalAlignment="Center" Margin="10,0,0,0" nbg:NameBasedGrid.Column="Left" nbg:NameBasedGrid.Row="RowSearchWord"/>
<TextBox Name="Search" nbg:NameBasedGrid.Column="Left" nbg:NameBasedGrid.Row="RowDictTitle" nbg:NameBasedGrid.ExtendToColumn="Right" Margin="50,2,10,2"/>
<!--Row 2-->
<ListBox Name="Words" nbg:NameBasedGrid.Column="Left" nbg:NameBasedGrid.Row="List" Margin="10"/>
</nbg:NameBasedGrid>
Advantage: You will be able to reference columns and rows (including column and row spans!) by name - no more counting of columns or rows, no more updating column or row spans when the layout changes.
Disadvantage: You will need to explicitly state names for all columns and rows, as numerical references are not supported at all in NameBasedGrid.
Nice idea but since the Grid.Row attached property is an integer this is not possible.
See http://msdn.microsoft.com/en-us/library/system.windows.controls.grid.row.aspx
However, it may possible to create a helper that takes the name of the grid row, finds the row object and returns its row index.
I was looking for the same thing. Could not find exacly what I was looking for so i came up with my own solution using attached properties.
I created a specialized grid with attached properties for RowName and ColumnName.
(In this example i only implemented RowName)
using System.Windows;
using System.Windows.Controls;
namespace GridNamedRows.CustomControl
{
public class MyGrid: Grid
{
public static readonly DependencyProperty RowNameProperty =
DependencyProperty.RegisterAttached(
"RowName",
typeof(string),
typeof(MyGrid),
new FrameworkPropertyMetadata(
"",
FrameworkPropertyMetadataOptions.AffectsParentArrange,
new PropertyChangedCallback(RowNameChanged)),
new ValidateValueCallback(IsStringNotNull));
private static bool IsStringNotNull(object value)
{
return (value as string) != null;
}
private static void RowNameChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue == null)
{
return;
}
if (!(d is UIElement)) return;
Grid parent = ((FrameworkElement)d).Parent as Grid;
if (parent == null) return;
//Find rowname
for (int i = 0; i < parent.RowDefinitions.Count; i++)
{
if (parent.RowDefinitions[i].Name == e.NewValue.ToString())
{
Grid.SetRow((UIElement)d, i);
break;
}
}
}
public static string GetRowName(DependencyObject target)
{
return (string)target.GetValue(RowNameProperty);
}
public static void SetRowName(DependencyObject target, string value)
{
target.SetValue(RowNameProperty, value);
}
}
}
It can be used in xaml like this.
<Window xmlns:CustomControl="clr-namespace:GridNamedRows.CustomControl" x:Class="GridNamedRows.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">
<CustomControl:MyGrid>
<Grid.RowDefinitions>
<RowDefinition Name="firstRow"/>
<RowDefinition Name="secondRow"/>
<RowDefinition Name="thirdRow"/>
</Grid.RowDefinitions>
<TextBox Text="one" CustomControl:MyGrid.RowName="secondRow"/>
<TextBox Text="two" Grid.Row="2"/>
<TextBox Text="three" CustomControl:MyGrid.RowName="firstRow"/>
</CustomControl:MyGrid>
</Window>
It does not display correctly in the designer but works in runtime.
Along the lines of the other answers I came up with this attached property solution that does not require using a custom Grid.
The code is largely redundant (for row & column) and can be used like this:
<Grid>
<Grid.RowDefinitions>
<RowDefinition x:Name="ThisRow"/>
<RowDefinition x:Name="ThatRow"/>
<RowDefinition x:Name="AnotherRow"/>
</Grid.RowDefinitions>
<TextBlock helpers:GridHelper.RowName="ThisRow" Text="..."/>
<TextBlock helpers:GridHelper.RowName="AnotherRow" Text="..."/>
<TextBlock helpers:GridHelper.RowName="ThatRow" Text="..."/>
</Grid>
GridHelper.cs:
public class GridHelper
{
public static string GetRowName(DependencyObject obj)
{
return (string)obj.GetValue(RowNameProperty);
}
public static void SetRowName(DependencyObject obj, string value)
{
obj.SetValue(RowNameProperty, value);
}
public static readonly DependencyProperty RowNameProperty =
DependencyProperty.RegisterAttached("RowName", typeof(string), typeof(GridHelper), new FrameworkPropertyMetadata(string.Empty, GridHelper.OnRowNamePropertyChanged));
public static void OnRowNamePropertyChanged(object sender, DependencyPropertyChangedEventArgs e)
{
var name = e.NewValue?.ToString();
if (string.IsNullOrEmpty(name)) return;
if (!(sender is FrameworkElement fe)) return;
if (!(fe.Parent is Grid grid)) return;
for (int i = 0; i < grid.RowDefinitions.Count; i++)
{
var rd = grid.RowDefinitions[i];
if (rd.Name.Equals(name))
{
Grid.SetRow(fe, i);
return;
}
}
throw new ArgumentException("Invalid RowName: " + name);
}
public static string GetColumnName(DependencyObject obj)
{
return (string)obj.GetValue(ColumnNameProperty);
}
public static void SetColumnName(DependencyObject obj, string value)
{
obj.SetValue(ColumnNameProperty, value);
}
public static readonly DependencyProperty ColumnNameProperty =
DependencyProperty.RegisterAttached("ColumnName", typeof(string), typeof(GridHelper), new FrameworkPropertyMetadata(string.Empty, GridHelper.OnColumnNamePropertyChanged));
public static void OnColumnNamePropertyChanged(object sender, DependencyPropertyChangedEventArgs e)
{
var name = e.NewValue?.ToString();
if (string.IsNullOrEmpty(name)) return;
if (!(sender is FrameworkElement fe)) return;
if (!(fe.Parent is Grid grid)) return;
for (int i = 0; i < grid.ColumnDefinitions.Count; i++)
{
var cd = grid.ColumnDefinitions[i];
if (cd.Name.Equals(name))
{
Grid.SetColumn(fe, i);
return;
}
}
throw new ArgumentException("Invalid ColumnName: " + name);
}
}
Note: This also may not work in the designer - I've never tried using it...
I want to clear all text box value when i pressed a button.I used this code that is fine work in winform but when i am trying to use this same code in wpf then error occured at this.Controls position.Here is the code.Please give me a solution.
foreach (Control c in this.Controls)
if (c is TextBox)
(c as TextBox).Clear();
I recommend looking into the MVVM pattern for WPF to solve your question.
By binding a textbox and button in your view (XAML) to a view model (class) you can clear the textbox values directly in the button command. There are many good MVVM frameworks like: Cinch and MVVM light to get you started.
Here is a sample that uses Cinch, but what's important is:
1. TextBox in row 0 uses TwoWay binding to Text1
2. TextBox in row 1 uses TwoWay binding to Text2
3. Button in row 2 uses Command binding to Clearcommand that sets Text1 and Text2 to string.Empty
Here is the view:
<Window x:Class="TextboxClear.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:meffed="clr-namespace:MEFedMVVM.ViewModelLocator;assembly=MEFedMVVM.WPF"
meffed:ViewModelLocator.ViewModel="MainWindowViewModel"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Text="{Binding Path=Text1, Mode=TwoWay}"/>
<TextBox Grid.Row="1" Text="{Binding Path=Text2, Mode=TwoWay}"/>
<Button Grid.Row="2" Content="Clear" Command="{Binding Path=ClearCommand}"/>
</Grid>
</Window>
Here is the view model:
using System;
using System.ComponentModel.Composition;
using Cinch;
using MEFedMVVM.ViewModelLocator;
namespace TextboxClear.ViewModels
{
[ExportViewModel("MainWindowViewModel")]
[PartCreationPolicy(CreationPolicy.Shared)]
public class MainWindowViewModel : ViewModelBase
{
[ImportingConstructor]
public MainWindowViewModel()
{
ClearCommand = new SimpleCommand<Object, Object>(CanExecuteClearCommand, ExecuteClearCommand);
}
private string _text1 = string.Empty;
public string Text1
{
get
{
return _text1;
}
set
{
_text1 = value;
NotifyPropertyChanged("Text1");
}
}
private string _text2 = string.Empty;
public string Text2
{
get
{
return _text2;
}
set
{
_text2 = value;
NotifyPropertyChanged("Text2");
}
}
public SimpleCommand<Object, Object> ClearCommand { get; private set; }
private void ExecuteClearCommand(Object args)
{
Text1 = string.Empty;
Text2 = string.Empty;
}
private bool CanExecuteClearCommand(Object args)
{
return true;
}
}
}
Use VisualTreeHelper.GetChild(). For example, if your textboxes are inside a StackPanel called StackPanelNew, use
for (int i = 0;i < VisualTreeHelper.GetChildrenCount(this.StackPanelNew);i++) {
TextBox txt = VisualTreeHelper.GetChild(this.StackPanelNew, i) as TextBox;
if (txt != null)
{
//do stuff
}
}