I have a ListBox with several items on it (TextBlocks, Images and so on), what I'm trying to do is access an element by it's name at a specific ListBox index.
I know the element name and the index i need to access, in this case I need to change the visibility property of an image control to collapsed.
I've looked at a few examples using VisualTreeHelper
here but they were only to access element by name, not by name and index, which is what i need to do but have not been able to.
Thanks, Bob.
I implemented a small demo to emphasize data binding using MVVM patern.
In this example I toggle the TextBlock
visibility using ShowTextbox property bound to the TextBlock.Visibility
by un/checking the Checkbox
.
App.xaml.cs
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var mainViewModel = new MainViewModel
{
ListItems = new ObservableCollection<MyModel>
{
new MyModel
{
MyPropertyText = "hello",
ShowText = true,
ShowTextbox = Visibility.Visible
}
}
};
var app = new MainWindow() {DataContext = mainViewModel};
app.Show();
}
}
MainWindow.xaml
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfApplication1="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ListBox HorizontalAlignment="Left" Height="148" VerticalAlignment="Top" Width="299" Margin="30,57,0,0" ItemsSource="{Binding Path=ListItems}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Path=MyPropertyText}" Visibility="{Binding Path=ShowTextbox}"/>
<CheckBox IsChecked="{Binding ShowText}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
MainWindow.cs
public partial class MainWindow : Window
{
private MainViewModel _mainViewModel;
public MainWindow()
{
InitializeComponent();
}
}
MainViewModel.cs
public class MainViewModel : ObservableObject
{
public ObservableCollection<MyModel> ListItems
{
get { return _listItems; }
set
{
_listItems = value;
RaisePropertyChanged("ListItems");
}
}
}
MyModel.cs
public class MyModel : ObservableObject
{
private string _myPropertyText;
private bool _showText;
private Visibility _showTextbox;
public string MyPropertyText
{
get { return _myPropertyText; }
set
{
_myPropertyText = value;
RaisePropertyChanged("MyPropertyText");
}
}
public bool ShowText
{
get { return _showText; }
set
{
_showText = value;
RaisePropertyChanged("ShowText");
ShowTextbox = value ? Visibility.Visible : Visibility.Collapsed;
}
}
public Visibility ShowTextbox
{
get { return _showTextbox; }
set
{
_showTextbox = value;
RaisePropertyChanged("ShowTextbox");
}
}
}
ObservableObject.cs
public class ObservableObject : INotifyPropertyChanged
{
#region Constructor
public ObservableObject() { }
#endregion // Constructor
#region RaisePropertyChanged
/// <summary>
/// Raises this object's PropertyChanged event.
/// </summary>
/// <param name="propertyName">The property that has a new value.</param>
protected virtual void RaisePropertyChanged(string propertyName = "")
{
VerifyPropertyName(propertyName);
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
#endregion
#region Debugging Aides
/// <summary>
/// Warns the developer if this object does not have
/// a public property with the specified name. This
/// method does not exist in a Release build.
/// </summary>
[Conditional("DEBUG")]
[DebuggerStepThrough]
public void VerifyPropertyName(string propertyName)
{
// If you raise PropertyChanged and do not specify a property name,
// all properties on the object are considered to be changed by the binding system.
if (String.IsNullOrEmpty(propertyName))
return;
// Verify that the property name matches a real,
// public, instance property on this object.
if (TypeDescriptor.GetProperties(this)[propertyName] == null)
{
string msg = "Invalid property name: " + propertyName;
if (this.ThrowOnInvalidPropertyName)
throw new ArgumentException(msg);
else
Debug.Fail(msg);
}
}
/// <summary>
/// Returns whether an exception is thrown, or if a Debug.Fail() is used
/// when an invalid property name is passed to the VerifyPropertyName method.
/// The default value is false, but subclasses used by unit tests might
/// override this property's getter to return true.
/// </summary>
protected virtual bool ThrowOnInvalidPropertyName { get; private set; }
#endregion // Debugging Aides
#region INotifyPropertyChanged Members
/// <summary>
/// Raised when a property on this object has a new value.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
#endregion // INotifyPropertyChanged Members
}
I ended up going about it in a slightly different way, based on the example found here and using the elements' tag property instead, just changing the element from a TextBlock
to an Image
This way i could bind the element Tag, and use that later on do target the needed items.
private void SearchVisualTree(DependencyObject targetElement, string _imageTag)
{
var count = VisualTreeHelper.GetChildrenCount(targetElement);
if (count == 0)
return;
for (int i = 0; i < count; i++)
{
var child = VisualTreeHelper.GetChild(targetElement, i);
if (child is Image)
{
Image targetItem = (Image)child;
if (targetItem.Tag as string == _imageTag && targetItem.Visibility == Visibility.Visible)
{
targetItem.Visibility = Visibility.Collapsed;
return;
}
}
else
{
SearchVisualTree(child, _imageTag);
}
}
}
Hopefully this helps someone else in the future...