WPF ItemControls do not show all items - wpf

What I'm doing is quite simple. I`m hosting list of UserControls in an ItemControl.
I made up a test application with a simple TextBox as a UserControl, it works as expected. But when I incorporate this in my main code, the ItemControl does not display all the Text Boxes. It always limits it to 3, and the values in the text boxes don`t get updated.
Is the IDE limiting how many UserControls I can view in the designer when I have other controls? When I execute the code, I do see all the UserControls. I just need to show 6 and not 100s.I would like to see all the UserControls in the designer in my main code and not just the test application.
The code between the test application and my main application is the same.
Here is the code, I`m attaching the pictures of what I see in the Designer.
/// <summary>
/// View Model Class for the MainWindow.
/// </summary>
class AViewModel : INotifyPropertyChanged
{
public AViewModel()
{
ZList = new ObservableCollection<StringObject>
{
new StringObject {Value = "1"},
new StringObject {Value = "2"},
new StringObject {Value = "3"},
new StringObject {Value = "4"},
new StringObject {Value = "5"},
new StringObject {Value = "6"},
new StringObject {Value = "7"},
};
}
private ObservableCollection<StringObject> zlist;
public ObservableCollection<StringObject> ZList
{
get { return zlist; }
set
{
zlist = value;
OnPropertyChanged("ZList");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
public class StringObject
{
public string Value { get; set; }
}
}
XML of Main Window
<Window x:Class="IssueWPF.MainWindow"
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:local="clr-namespace:IssueWPF"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:AViewModel></local:AViewModel>
</Window.DataContext>
<Grid>
<Viewbox>
<ItemsControl ItemsSource="{Binding ZList}" Height="220" Margin="0">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Value}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" IsItemsHost="True" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Viewbox>
</Grid>
</Window>

Related

WPF Binding not refreshing if Equals and GetHashCode [duplicate]

I have a project, where I bind a checkbox's IsChecked property with a get/set in the codebehind. However, when the application loads, it doesn't update, for some reason. Intrigued, I stripped it down to its basics, like this:
//using statements
namespace NS
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private bool _test;
public bool Test
{
get { Console.WriteLine("Accessed!"); return _test; }
set { Console.WriteLine("Changed!"); _test = value; }
}
public MainWindow()
{
InitializeComponent();
Test = true;
}
}
}
XAML:
<Window x:Class="TheTestingProject_WPF_.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" DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<Viewbox>
<CheckBox IsChecked="{Binding Path=Test, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</Viewbox>
</Grid>
And, lo and behold, when I set it to true, it did not update!
Anyone can come up with a fix, or explain why?
Thanks, it'd be appreciated.
In order to support data binding, your data object must implement INotifyPropertyChanged
Also, it's always a good idea to Separate Data from Presentation
public class ViewModel: INotifyPropertyChanged
{
private bool _test;
public bool Test
{ get { return _test; }
set
{
_test = value;
NotifyPropertyChanged("Test");
}
}
public PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
<Window x:Class="TheTestingProject_WPF_.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>
<Viewbox>
<CheckBox IsChecked="{Binding Path=Test, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</Viewbox>
</Grid>
Code Behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel{Test = true};
}
}

C# - Click event on multiple UIElement types collection

I'm displaying a collection of UI elements of different types (Rectangles and Images) in the canvas. Both derive from the UIElement type.
<ItemsControl ItemsSource="{Binding UiElementsCollection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas HorizontalAlignment="Left" VerticalAlignment="Top">
</Canvas>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
Everything displays fine, but I want to have an event trigger to mouse events - when I click on/drag the specific element over the canvas I would like to receive this object (Rectangle or Image). I would like to do it the MVVM pattern. How can I do it?
You could implement an attached behaviour that hooks up the PreviewMouseLeftButtonDown of all UIElements in your source collection to a command of the view model:
public class MyBehavior
{
public static readonly DependencyProperty MouseLeftButtonDownCommandProperty
= DependencyProperty.RegisterAttached("MouseLeftButtonDownCommand",
typeof(ICommand), typeof(MyBehavior), new FrameworkPropertyMetadata(new PropertyChangedCallback(OnMouseLeftButtonDownCommandPropertyChanged)));
public static void SetMouseLeftButtonDownCommand(UIElement element, ICommand value)
{
element.SetValue(MouseLeftButtonDownCommandProperty, value);
}
public static ICommand GetMouseLeftButtonDownCommand(UIElement element)
{
return (ICommand)element.GetValue(MouseLeftButtonDownCommandProperty);
}
private static void OnMouseLeftButtonDownCommandPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
UIElement element = d as UIElement;
element.PreviewMouseLeftButtonDown += Element_MouseLeftButtonDown;
}
private static void Element_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
UIElement element = sender as UIElement;
ICommand command = GetMouseLeftButtonDownCommand(element);
if (command != null)
command.Execute(element);
}
}
View:
<Window x:Class="WpfApplication1.Window6"
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:local="clr-namespace:WpfApplication1"
mc:Ignorable="d"
Title="Window6" Height="300" Width="300">
<Window.DataContext>
<local:ViewModel />
</Window.DataContext>
<Grid>
<ItemsControl ItemsSource="{Binding UiElementsCollection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas HorizontalAlignment="Left" VerticalAlignment="Top">
</Canvas>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="local:MyBehavior.MouseLeftButtonDownCommand"
Value="{Binding DataContext.TheCommand, RelativeSource={RelativeSource AncestorType=Window}}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</Grid>
</Window>
View Model:
public class ViewModel
{
public ObservableCollection<UIElement> UiElementsCollection { get; } = new ObservableCollection<UIElement>()
{
new Button { Content = "btn" },
new Border { Background = Brushes.Yellow, Width = 10, Height = 10 }
};
public ICommand TheCommand { get; } = new DelegateCommand<UIElement>(element =>
{
MessageBox.Show(element.GetType().ToString());
});
}
You will obviously need an implementation of the ICommand interface. The DelegateCommand class is available in Prism: https://github.com/PrismLibrary/Prism/blob/master/Source/Prism/Commands/DelegateCommand.cs
Commands are pretty essential to an MVVM application though so you probably knew this already. You could refer to the following blog post for more information about how to handle events using commands in MVVM: https://blog.magnusmontin.net/2013/06/30/handling-events-in-an-mvvm-wpf-application/

Binding Textbox to Two sources WPF

I have a text box, which default value I want to bind to Combo box selecteItem, and same time I want my text box to be binded to Mvvm object property?
I checked here but the multibinding confuse me.
I would prefer to have xaml solution for this issue.
Addition:
In combobox I will select an Account, that account contain some values (Amount), I want to display Amount, But need my text box to be bounded to mvvm model object element stAmount. so the user can change the amount selected by combobbox and then this modified or unchanged amount value could be stored to text box binded model-object element (stAmount)
Making use of INotifyPropertyChanged:
XAML
<Window x:Class="INotifyPropertyChangedExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Title="INotifyPropertyChanged Example" Width="380" Height="100">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Label Content="Account Name:" />
<Label Grid.Row="1" Grid.Column="0" Content="Account Balance:" />
<ComboBox Grid.Row="0" Grid.Column="1" Width="200" Height="25" ItemsSource="{Binding AccountsCollection}" SelectedItem="{Binding SelectedAccount}" DisplayMemberPath="Name" />
<TextBox Grid.Column="1" Grid.Row="1" Width="200" Height="25" Text="{Binding SelectedAccount.Balance}" />
</Grid>
</Window>
C#
namespace INotifyPropertyChangedExample
{
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
public partial class MainWindow : Window, INotifyPropertyChanged
{
private ObservableCollection<Account> acctountsCollection;
public ObservableCollection<Account> AccountsCollection
{
get
{
return this.acctountsCollection;
}
set
{
this.acctountsCollection = value;
OnPropertyChanged();
}
}
private Account selectedAccount;
public Account SelectedAccount
{
get
{
return this.selectedAccount;
}
set
{
this.selectedAccount = value;
OnPropertyChanged();
}
}
public MainWindow()
{
InitializeComponent();
this.AccountsCollection = new ObservableCollection<Account>()
{
new Account { Id = 1, Name = "My super account", Balance = 123.45 },
new Account { Id = 2, Name = "My super account 2", Balance = 543.21 },
};
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class Account
{
public int Id { get; set; }
public string Name { get; set; }
public double Balance { get; set; }
}
}
In this example we bind an ObservableCollection of Account objects to your ComboBox and keep track of which Account is selected through the SelectedItem property. We bind the TextBox text property to the Balance property of the selected Account object. Therefore when then selected Account object changes the value displayed in the TextBox changes to reflect the Balance of the Account.
Additionally if you change the value in the TextBox, the Balance value of the Account object is updated.
It seems to me like you want bind your textbox to the selected value property in your viewmodel not the combo box.
using System.Collections.ObjectModel;
using System.Windows;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public ObservableCollection<string> Items
{
get { return (ObservableCollection<string>)GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
public static readonly DependencyProperty ItemsProperty =
DependencyProperty.Register("Items", typeof(ObservableCollection<string>), typeof(MainWindow), new PropertyMetadata(null));
public string SelectedValue
{
get { return (string)GetValue(SelectedValueProperty); }
set { SetValue(SelectedValueProperty, value); }
}
public static readonly DependencyProperty SelectedValueProperty =
DependencyProperty.Register("SelectedValue", typeof(string), typeof(MainWindow), new PropertyMetadata(null));
public MainWindow()
{
InitializeComponent();
Items = new ObservableCollection<string>();
Items.Add("Value 1");
Items.Add("Value 2");
Items.Add("Value 3");
Items.Add("Value 4");
Items.Add("Value 5");
Items.Add("Value 6");
}
}
}
and the xaml
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Title="MainWindow" Height="350" Width="525">
<Grid >
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<ComboBox Grid.Row="0" ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedValue}"/>
<TextBox Grid.Row="1" Text="{Binding SelectedValue}"/>
</Grid>
</Window>

WPF ComboBox not Firing on selection

I have a ComboBox, shown below. Why isn't the code-behind always firing?
XAML:
<ComboBox Height="23"
Name="cbAppendCreate"
VerticalAlignment="Top"
Width="120"
Margin="{StaticResource ConsistentMargins}"
ItemsSource="{Binding Path=CbCreateAppendItems}"
SelectedValue="{Binding Path=CbAppendCreate,UpdateSourceTrigger=PropertyChanged}" />
CodeBehind:
private string cbAppendCreate;
public string CbAppendCreate {
get {
//....
return cbAppendCreate
}
set { //This doesn't fire when selecting the first of 2 Items,
//but always fires when selecting the 2nd of two items
//....
cbAppendCreate = value;
}
}
I'll post my working code here, it's very simple. I've just created a default WPF app using VS2012 template. Here's MainWindow.xaml content:
<Window x:Class="
WpfApplication1.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">
<StackPanel>
<ComboBox Height="23"
Name="cbAppendCreate"
VerticalAlignment="Top"
Width="120"
ItemsSource="{Binding Path=CbCreateAppendItems}"
SelectedValue="{Binding Path=CbAppendCreate,UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Text="{Binding CbAppendCreate}"></TextBlock>
</StackPanel>
Here's code-behind:
namespace WpfApplication1
{
public class DataSource
{
public List<string> CbCreateAppendItems { get; set; }
public string CbAppendCreate { get; set; }
public DataSource()
{
CbCreateAppendItems = new List<string>() { "create", "append" };
}
}
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new DataSource();
}
}
}
When I select different values in combo-box, the TextBlock updates to the same value, hence the VM's property is also updated.

Trying to bind TextBlock which is inside the DataTemplate of an accordion control in Silverlight. Text is not visible when I run the application

I amsomewhat new to Silverlight, What I want to do is to show the Title in the accordion control which is bound from the property of that user control. I have a TextBlock which is inside the DataTemplate of an Accordion control in Silverlight. When I run the application, text is coming blank and nothing is displayed in the Accordion title.
<UserControl x:Class="SilverlightApplication1.SilverlightControl1"
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:layoutToolkit="clr- namespace:System.Windows.Controls;assembly=System.Windows.Controls.Layout.Toolkit"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400" DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid x:Name="LayoutRoot" Background="White">
<layoutToolkit:Accordion x:Name="accordionFilter" HorizontalAlignment="Stretch" SelectionMode="ZeroOrMore">
<layoutToolkit:AccordionItem MinHeight="0" MaxHeight="120" IsSelected="True">
<layoutToolkit:AccordionItem.HeaderTemplate>
<DataTemplate>
<Grid Height="22" VerticalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock
Text="{Binding MainPageSelectedText}"
Width="150"></TextBlock>
</Grid>
</DataTemplate>
</layoutToolkit:AccordionItem.HeaderTemplate>
</layoutToolkit:AccordionItem>
</layoutToolkit:Accordion>
</Grid>
</UserControl>
using System;
using System.ComponentModel;
using System.Windows.Controls;
namespace SilverlightApplication1
{
public partial class SilverlightControl1 : UserControl, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string name)
{
PropertyChangedEventHandler ph = this.PropertyChanged;
if (ph != null)
ph(this, new PropertyChangedEventArgs(name));
}
public SilverlightControl1()
{
InitializeComponent();
MainPageSelectedText = "Sample Text";
}
public string MainPageSelectedText
{
get { return _MainPageSelectedText; }
set
{
string myValue = value ?? String.Empty;
if (_MainPageSelectedText != myValue)
{
_MainPageSelectedText = value;
OnPropertyChanged("MainPageSelectedText");
}
}
}
private string _MainPageSelectedText;
}
}
On your data template level you don't have direct access to the user control's DataContext so your binding should look sth like:
Text="{Binding MainPageSelectedText, ElementName=MyUserControl}"
assuming that you will set Name/x:Name of your user control to MyUserControl.

Resources