How to make a custom control WPF Resource Dictionary with MVVMLight RelayCommands - wpf

Can i / How can I attach commands to a custom control i made and put it into a Resource Dictionary in WPF? I would like to not define this control in multiple places and also not have the code in code-behind file of the XAML for it. Here is the first rendition of my control, its basically a re-orderable listbox of checkboxes. Unchecked items cannot be at the top of the list.
<ListBox Name="listBoxZone" Grid.Row="3" HorizontalContentAlignment="Stretch" Height="200"
ItemsSource="{Binding TheList}" >
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderThickness="0.5" BorderBrush="DarkGray">
<Grid Height="30">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<CheckBox Content="{Binding Name}" IsChecked="{Binding IsChecked}" Click="Reorder_Click" Grid.RowSpan="2" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Width="Auto"/>
<Button Click="UpDownClick" Name="Up" Grid.Row="0" Grid.Column="1" HorizontalAlignment="Right" Margin="1" ToolTip="Up" BorderBrush="{x:Null}" Background="{x:Null}">
<Image Source="/Resources/Icons/sort_up.png"/>
</Button>
<Button Name="Down" Click="UpDownClick" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Right" Margin="1" ToolTip="Down" BorderBrush="{x:Null}" Background="{x:Null}">
<Image Source="/Resources/Icons/sort_down.png"/>
</Button>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And what's currently just in code-behind :
public TaskRolesView()
{
InitializeComponent();
//remove later when i figure this out!
CreateCheckBoxList();
this.DataContext = this;
}
private System.Collections.ObjectModel.ObservableCollection<StoredProc> _theList;
public System.Collections.ObjectModel.ObservableCollection<StoredProc> TheList
{
get { return _theList; }
set
{
if (value != _theList)
{
_theList = value;
FirePropertyChanged("TheList");
}
}
}
public void CreateCheckBoxList()
{
TheList = new System.Collections.ObjectModel.ObservableCollection<StoredProc>();
StoredProc mea = new StoredProc();
mea.Name = "MEA";
mea.IsChecked = true;
StoredProc valic = new StoredProc();
valic.Name = "VALIC";
valic.IsChecked = true;
StoredProc axa = new StoredProc();
axa.Name = "AXA";
axa.IsChecked = true;
StoredProc fidelity = new StoredProc();
fidelity.Name = "Fidelity";
fidelity.IsChecked = true;
StoredProc first = new StoredProc();
first.Name = "Step 1";
first.IsChecked = true;
StoredProc second = new StoredProc();
second.Name = "Step 2";
second.IsChecked = false;
StoredProc last = new StoredProc();
last.Name = "Last";
last.IsChecked = false;
StoredProc another = new StoredProc();
another.Name = "another";
another.IsChecked = false;
StoredProc onemore = new StoredProc();
onemore.Name = "onemore";
onemore.IsChecked = false;
TheList.Add(mea);
TheList.Add(valic);
TheList.Add(axa);
TheList.Add(fidelity);
TheList.Add(first);
TheList.Add(second);
TheList.Add(last);
TheList.Add(another);
TheList.Add(onemore);
}
public class StoredProc
{
public string Name { get; set; }
public bool IsChecked { get; set; }
}
private void UpDownClick(object sender, RoutedEventArgs e)
{
Button button = sender as Button;
StoredProc sp = button.DataContext as StoredProc;
int oldLocation = TheList.IndexOf(sp);
if (oldLocation > 0 && button.Name == "Up")
{
if (oldLocation > 0)
{
if (sp.IsChecked || (TheList[oldLocation - 1].IsChecked == sp.IsChecked))
{
TheList.RemoveAt(oldLocation);
TheList.Insert(oldLocation - 1, sp);
}
}
}
if (oldLocation < TheList.Count - 1 && button.Name == "Down")
{
if (oldLocation + 1 <= TheList.Count)
{
if (sp.IsChecked == false || (TheList[oldLocation + 1].IsChecked == sp.IsChecked))
{
TheList.RemoveAt(oldLocation);
TheList.Insert(oldLocation + 1, sp);
}
}
}
}
private void Reorder_Click(object sender, RoutedEventArgs e)
{
CheckBox checkBox = sender as CheckBox;
IEnumerable<StoredProc> sort;
sort = TheList.OrderByDescending(item => item.IsChecked);
System.Collections.ObjectModel.ObservableCollection<StoredProc> temp = new System.Collections.ObjectModel.ObservableCollection<StoredProc>();
foreach (var item in sort)
{
temp.Add(item);
}
TheList.Clear();
TheList = temp;
}
#region FirePropertyChanged
/// <summary>
/// Property Changed handler
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Needed to update view model
/// </summary>
/// <param name="propertyName"></param>
protected void FirePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion

Related

How to prevent autoscrolling up if new row added at the beginnging

I have a DataGrid with around 500 rows. If I am scrolled down to let's row no. 200 and a new row is added to the top (beginning of the collection) it automatically scrolls up to the first row so I have to scroll down again manually.
How can I prevent this to happen?
Here is a sample app to illustrate the problem:
MainWindow.xaml.cs:
using System.Collections.ObjectModel;
using System.Windows;
namespace Testnamespace
{
public class TestClass
{
private int price;
private int qty;
public int Price { get => price; set => price = value; }
public int Qty { get => qty; set => qty = value; }
public TestClass(int price,int qty)
{
this.Price = price;
this.Qty = qty;
}
}
public partial class MainWindow : Window
{
private ObservableCollection<TestClass> data = new ObservableCollection<TestClass>() { new TestClass(3, 1), new TestClass(2, 1), new TestClass(1, 1) };
public ObservableCollection<TestClass> Data { get => data; set => data = value; }
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
}
private void InsertButton_Click(object sender, RoutedEventArgs e)
{
Data.Insert(0, new TestClass(Data.Count, 1));
}
}
}
XAML:
<Window x:Class="Testnamespace.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:Testnamespace"
mc:Ignorable="d"
Title="MainWindow" Height="300" Width="200">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="80"/>
<RowDefinition MinHeight="80"/>
</Grid.RowDefinitions>
<Button x:Name="InsertButton" Content="Insert(0)" Click="InsertButton_Click" Grid.Column="0" Height="20" Width="50" Background="Red"/>
<DataGrid x:Name="dataGrid"
Grid.Row="1"
AutoGenerateColumns="False"
ItemsSource="{Binding Data, IsAsync=True}"
MaxWidth="800"
MaxHeight="1600"
Width="100"
Height="100"
>
<DataGrid.Columns>
<DataGridTextColumn Header="Price" Binding="{Binding Price}">
</DataGridTextColumn>
<DataGridTextColumn Header="Qty" Binding="{Binding Qty}">
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
This is how the app looks like:
Let's say you want to keep your eyes on price=1 to see if Qty has changed but in the meanwhile new rows are added (by clicking the Insert(0) button). In this case you lose focus because it keeps scrolling up, so you have to scroll down manually.
How can I prevent this to happen?
You could manually scroll another 18.96 DIP, which is the default Height of a DataGridRow:
private void InsertButton_Click(object sender, RoutedEventArgs e)
{
ScrollViewer scrollViewer = FindVisualChild<ScrollViewer>(dataGrid);
double offset = scrollViewer.VerticalOffset;
Data.Insert(0, new TestClass(Data.Count, 1));
if (offset > 0)
scrollViewer.ScrollToVerticalOffset(offset + 18.96);
}
private static T FindVisualChild<T>(Visual visual) where T : Visual
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(visual); i++)
{
Visual child = (Visual)VisualTreeHelper.GetChild(visual, i);
if (child != null)
{
T correctlyTyped = child as T;
if (correctlyTyped != null)
return correctlyTyped;
T descendent = FindVisualChild<T>(child);
if (descendent != null)
return descendent;
}
}
return null;
}

C# WPF ObservableCollection does not update Grid

My implementation of an ObservableCollection does not update my Grid and I don't know why:
The first XAML-Window 'UserControlStaff.xaml' displays the Grid where the user data can be seen. The second XAML-Window allows to add users and is called 'CreateUser.xaml'.
The code behind the XAML-Windows is attached to this post as well.
My Code goes as follows:
[XAML] UserControlStaff.xaml
<UserControl x:Class="MyApp.UserControlStaff"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="786">
<StackPanel x:Name="spStaff" Orientation="Vertical" Visibility="Visible">
<Button Background="Azure" Click="btnCreateUser_Click" HorizontalAlignment="Left" Width="786" Height="40">
<Bold>Mitarbeiter hinzufügen</Bold></Button>
<ScrollViewer MaxHeight="504">
<DataGrid Background="#DBDB72" ItemsSource="{Binding UserDataObject}" CanUserAddRows="false" Width="786" />
</ScrollViewer>
</StackPanel>
</UserControl>
[Code] UserControlStaff.xaml.cs
namespace MyApp
{
/// <summary>
/// Interaktionslogik für UserControlStaff.xaml
/// </summary>
public partial class UserControlStaff : UserControl
{
ObservableCollection<User> mUserDataObject = new ObservableCollection<User>();
public ObservableCollection<User> UserDataObject
{
get
{
return mUserDataObject;
}
}
public UserControlStaff()
{
InitializeComponent();
DataContext = this;
try
{
string connectionString = ConfigurationManager.ConnectionStrings["MyApp.Properties.Settings.ConString"].ConnectionString;
string queryString = string.Empty;
using (SqlConnection connection = new SqlConnection(connectionString))
{
queryString = "SELECT ID, username AS Username, Password AS Passwort, (SELECT role FROM Roles WHERE ID = t1.role) AS Rolle FROM Users t1 ORDER BY ID";
SqlCommand cmd = new SqlCommand(queryString, connection);
SqlDataAdapter sda = new SqlDataAdapter(cmd);
DataTable dt = new DataTable("Users");
sda.Fill(dt);
foreach (DataRow row in dt.Rows)
{
int pID = 0;
string pUsername = "";
string pPassword = "";
string pRole = "";
foreach (DataColumn col in dt.Columns)
{
if (col.ToString().Trim() == "ID")
{
pID = int.Parse(row[col].ToString());
}
else if (col.ToString().Trim() == "Username")
{
pUsername = row[col].ToString();
}
else if (col.ToString().Trim() == "Passwort")
{
pPassword = row[col].ToString();
}
else if (col.ToString().Trim() == "Rolle")
{
pRole = row[col].ToString();
}
}
// Show Users DB table in MainWindow
mUserDataObject.Add(new User { ID = pID, Username = pUsername, Password = pPassword, Role = pRole });
}
// Show Users DB table in MainWindow
//XAML Grid: Name="gridUsers"
//CS Code: gridUsers.DataContext = dt.DefaultView;
}
}
catch {
throw;
}
}
private void btnCreateUser_Click(object sender, RoutedEventArgs e)
{
CreateUser popup = new CreateUser();
popup.Show();
}
private void UpdateUserData()
{
// implement
}
private void DeleteUserData()
{
// implement
}
}
}
[XAML] CreateUser.xaml
<Window x:Class="MyApp.CreateUser"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Mitarbeiter hinzufügen" Height="300" Width="400">
<Grid>
<Label Content="Username" HorizontalAlignment="Left" Margin="10,44,0,0" VerticalAlignment="Top" Width="120" Height="30"/>
<Label Content="Passwort" HorizontalAlignment="Left" Margin="10,88,0,0" VerticalAlignment="Top" Width="120" Height="30"/>
<Label Content="Rolle" HorizontalAlignment="Left" Margin="10,132,0,0" VerticalAlignment="Top" Width="120" Height="30"/>
<TextBox HorizontalAlignment="Left" Height="30" Margin="150,44,0,0" TextWrapping="Wrap" Name="TextBoxUsername" Text="{Binding Path=Username}" VerticalAlignment="Top" Width="180"/>
<TextBox HorizontalAlignment="Left" Height="30" Margin="150,88,0,0" TextWrapping="Wrap" Name="TextBoxPassword" Text="{Binding Path=Password}" VerticalAlignment="Top" Width="180"/>
<ComboBox HorizontalAlignment="Left" Height="30" Margin="150,132,0,0" VerticalAlignment="Top" Width="180" Loaded="ComboBox_Loaded" SelectionChanged="ComboBox_SelectionChanged"/>
<Button Click="btnSaveUserData_Click" Content="Speichern" HorizontalAlignment="Left" Margin="217,219,0,0" VerticalAlignment="Top" Height="30" Width="75"/>
<Button Click="btnCloseWindow_Click" Content="Abbrechen" HorizontalAlignment="Left" Margin="297,219,0,0" VerticalAlignment="Top" Height="30" Width="75"/>
</Grid>
</Window>
[Code] CreateUser.xaml.cs
namespace MyApp
{
/// <summary>
/// Interaktionslogik für CreateUser.xaml
/// </summary>
public partial class CreateUser : Window
{
User userObject;
public CreateUser()
{
InitializeComponent();
this.userObject = new User();
}
private void ComboBox_Loaded(object sender, RoutedEventArgs e)
{
// ToDO: Rollenzuweisung automatisieren aus Datenbanktabelle MyApp.Roles
List<string> data = new List<string>();
data.Add("Chef");
data.Add("Restaurantmitarbeiter");
data.Add("Fahrer");
// ... Get the ComboBox reference.
var comboBox = sender as ComboBox;
// ... Assign the ItemsSource to the List.
comboBox.ItemsSource = data;
// ... Make the second item selected.
comboBox.SelectedIndex = 1;
}
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
// ... Get the ComboBox.
var comboBox = sender as ComboBox;
// ... Set SelectedItem as Window Title.
string value = comboBox.SelectedItem as string;
//this.Title = "Selected: " + value;
this.userObject.Role = value;
}
private void btnSaveUserData_Click(object sender, RoutedEventArgs e)
{
try
{
this.userObject.Username = TextBoxUsername.Text;
this.userObject.Password = TextBoxPassword.Text;
int UserRole;
// ToDO: Rollenzuweisung automatisieren aus Datenbanktabelle MyApp.Roles
if (this.userObject.Role == "Chef")
{
UserRole = 1;
}
else if (this.userObject.Role == "Restaurantmitarbeiter")
{
UserRole = 2;
}
else if (this.userObject.Role == "Fahrer")
{
UserRole = 3;
}
else
{
UserRole = 2; // Default UserRole is "Restaurantmitarbeiter"
}
if (this.userObject.Username.Trim() != "" && this.userObject.Password.Trim() != "")
{
CreateUserData(this.userObject.Username, this.userObject.Password, UserRole);
// ToDO: Update DataGrid in UserControlStaff
this.Close();
MessageBox.Show("Mitarbeiter hinzugefügt!");
} else {
MessageBox.Show("Bitte Username und Passwort eingeben.");
}
}
catch { throw; }
}
private void CreateUserData(string pUsername, string pPassword, int pRole)
{
string connectionString = ConfigurationManager.ConnectionStrings["MyApp.Properties.Settings.ConString"].ConnectionString;
string queryString = "INSERT INTO Users (ID, username, password, role) VALUES ((SELECT TOP 1 ID+1 FROM Users ORDER BY ID DESC), '" + pUsername + "', '" + pPassword + "', '" + pRole + "')";
SqlConnection connection = new SqlConnection(connectionString);
SqlCommand cmd = new SqlCommand(queryString, connection);
SqlDataAdapter sda = new SqlDataAdapter(cmd);
DataTable dt = new DataTable("Users");
sda.Fill(dt);
string UserRole;
if (pRole == 1)
{
UserRole = "Chef";
}
else if (pRole == 2)
{
UserRole = "Restaurantmitarbeiter";
}
else if (pRole == 3)
{
UserRole = "Fahrer";
}
else
{
UserRole = "Restaurantmitarbeiter"; // Default UserRole is "Restaurantmitarbeiter"
}
// Add to Observable Collection
// ToDO
// mUserDataObject.Add(new User { ID = pID, Username = pUsername, Password = pPassword, Role = pRole });
}
private void btnCloseWindow_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
}
}

How to show next n items using WPF ListBox or ScrollView?

I have list box with 10 items.
By default vertical scroll is enabled and i can see first 3 items.
Customer wants me to add 2 buttons "UP" and "down" and on a button click list box should show next 3 items.
For example i want to show by "down" click item 4,item 5, item 6.
How its possible to do with default WPF controls listbox and scrollview?
You can use ScrollViewer.ScrollToVerticalOffset method (msdn).
Example:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="20" />
<RowDefinition Height="50" />
<RowDefinition Height="20" />
<RowDefinition Height="25" />
</Grid.RowDefinitions>
<Button x:Name="btnUp" Content="UP" Click="btnUp_Click" />
<ScrollViewer x:Name="scroll" Grid.Row="1">
<ListBox x:Name="lbData">
<ListBoxItem>Item1</ListBoxItem>
<ListBoxItem>Item2</ListBoxItem>
<ListBoxItem>Item3</ListBoxItem>
<ListBoxItem>Item4</ListBoxItem>
<ListBoxItem>Item5</ListBoxItem>
<ListBoxItem>Item6</ListBoxItem>
<ListBoxItem>Item7</ListBoxItem>
<ListBoxItem>Item8</ListBoxItem>
<ListBoxItem>Item9</ListBoxItem>
<ListBoxItem>Item10</ListBoxItem>
</ListBox>
</ScrollViewer>
<Button x:Name="btnDown" Content="Down" Click="btnDown_Click" Grid.Row="2" />
<StackPanel Grid.Row="3" Orientation="Horizontal">
<TextBlock Text="Start with:" Margin="2" />
<ComboBox x:Name="cbIndex" Loaded="cbIndex_Loaded" Margin="2" />
<Button x:Name="btnGo" Content="GO" Click="btnGo_Click" Margin="2" />
</StackPanel>
</Grid>
Code-behind:
private void btnUp_Click(object sender, RoutedEventArgs e)
{
scroll.ScrollToVerticalOffset(scroll.VerticalOffset - 50);
}
private void btnDown_Click(object sender, RoutedEventArgs e)
{
scroll.ScrollToVerticalOffset(scroll.VerticalOffset + 50);
}
public double GetOffset(int itemIndex)
{
double result = 0;
for (int i = 0; i < itemIndex; i++)
{
result += (lbData.Items[i] as ListBoxItem).ActualHeight;
}
return result;
}
private void cbIndex_Loaded(object sender, RoutedEventArgs e)
{
cbIndex.ItemsSource = Enumerable.Range(1, lbData.Items.Count);
}
private void btnGo_Click(object sender, RoutedEventArgs e)
{
scroll.ScrollToVerticalOffset(GetOffset(cbIndex.SelectedIndex));
}
Solution for horizontal list:
/// <summary>
/// Show next n items starting from first visible.
/// </summary>
public void ShowNext()
{
if (this.scrollviewer == null)
{
return;
}
var rightLimit = this.scrollviewer.HorizontalOffset + this.scrollviewer.ViewportWidth;
double horizontalOffset = 0;
foreach (var item in this.Items)
{
var container = this.ItemContainerGenerator.ContainerFromItem(item) as FrameworkElement;
if (container == null)
{
continue;
}
if (horizontalOffset + container.ActualWidth >= rightLimit)
{
// We found last item offset
break;
}
horizontalOffset += container.ActualWidth;
}
this.scrollviewer.ScrollToHorizontalOffset(horizontalOffset);
}
/// <summary>
/// Show previous n items starting last visible item.
/// </summary>
public void ShowPrevious()
{
if (this.scrollviewer == null)
{
return;
}
double horizontalOffset = 0;
foreach (var item in this.Items)
{
var container = this.ItemContainerGenerator.ContainerFromItem(item) as FrameworkElement;
if (container == null)
{
continue;
}
horizontalOffset += container.ActualWidth;
if (horizontalOffset >= this.scrollviewer.HorizontalOffset)
{
// We found last item offset
break;
}
}
horizontalOffset -= this.scrollviewer.ViewportWidth;
this.scrollviewer.ScrollToHorizontalOffset(horizontalOffset);
}
This code will show next\previous items from last visible in listbox.

About setbinding in canvas

<StackPanel Height="650" Width="650" Background="Green" HorizontalAlignment="Center" VerticalAlignment="Center">
<ItemsControl Name="Display_Test" Margin="10">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Background="Black" Width="600" Height="600"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding}" Margin="15" Height="500" Width="500"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
// c# code start ====================================================================
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
this.InitializeComponent();
Canvas canvasSample = new Canvas();
canvasSample.Width = 100;
canvasSample.Height = 100;
canvasSample.Background = new SolidColorBrush(Colors.LightBlue);
CanvasList.Add(canvasSample);
Canvas canvasSample2 = new Canvas();
canvasSample2.Width = 100;
canvasSample2.Height = 100;
canvasSample2.Background = new SolidColorBrush(Colors.LightGreen);
CanvasList.Add(canvasSample2);
Rectangle rectangleSample = new Rectangle();
rectangleSample.Width = 30;
rectangleSample.Height = 30;
RectangleList.Add(rectangleSample);
Rectangle rectangleSample2 = new Rectangle();
rectangleSample2.Width = 30;
rectangleSample2.Height = 30;
RectangleList.Add(rectangleSample2);
Display_Test.ItemsSource = CanvasList;
this.MouseDoubleClick += new MouseButtonEventHandler(MainWindow_MouseDoubleClick);
}
void MainWindow_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
for (int nCanvasNum = 0; nCanvasNum < CanvasList.Count; nCanvasNum++)
{
Binding conBinding = new Binding()
{
Source = RectangleList
};
CanvasList[nCanvasNum].DataContext = RectangleList;
CanvasList[nCanvasNum].SetBinding(ItemsControl.ItemsSourceProperty, conBinding);
}
}
ObservableCollection<Canvas> _canvasList = new ObservableCollection<Canvas>();
public ObservableCollection<Canvas> CanvasList
{
get { return _canvasList; }
set
{
_canvasList = value;
OnPropertyChanged(new PropertyChangedEventArgs("CanvasList"));
}
}
ObservableCollection<Rectangle> _rectangleList = new ObservableCollection<Rectangle>();
public ObservableCollection<Rectangle> RectangleList
{
get { return _rectangleList; }
set
{
_rectangleList = value;
OnPropertyChanged(new PropertyChangedEventArgs("RegtangleList"));
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
{
PropertyChanged(this, e);
}
}
#endregion
}
// c# code end ====================================================================
when i clicked mousebutton double I want to create RectangleList in CanvasList...
But I'm afraid I can't show that...I think that is problem because of setbinding...
I will wait for your answer....
CanvasList[nCanvasNum].SetBinding(ItemsControl.ItemsSourceProperty, conBinding); won't work bevause Canvas is not derived from ItemsControl and does not have ItemSource property. It has children property but it is not dependency property so you can't bind to it but you can add childrens manually. You can try to use ItemsControl instead of Canvas and set Canvas as a panel for ItemsControl the same way you did it to Show canvases

DataVirtualization and ListView UI freeze

I tried to find a solution with asynchronous DataProvider, but everywhere is bound to a static class, but I need a dynamic binding to different objects.
I tried to use the this solution but my UI still freezes. Anyone can explain why I get this behavior and how do I make UI active.
Application XAML file:
<Window x:Class="WpfApplication_async.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication_async"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<local:RecordValueDataTemplateSelector x:Key="myDataTemplateSelector"/>
<DataTemplate x:Key="ComboBoxTemplate">
<ComboBox Width="{Binding Path=ColumnDef.ColumnWidth,Mode=OneTime}"
ItemsSource="{Binding Path=viewEntityList,Mode=TwoWay}"
SelectedIndex="{Binding Path=Index, Mode=TwoWay}">
</ComboBox>
</DataTemplate>
<DataTemplate x:Key="TextBoxTemplate">
<TextBox Text="{Binding Path=Text,Mode=TwoWay}" Width="{Binding Path=ColumnDef.ColumnWidth,Mode=OneTime}"/>
</DataTemplate>
<DataTemplate x:Key="HeaderTextBlockTemplate">
<TextBlock Width="{Binding Path=ColumnWidth,Mode=OneTime}" Text="{Binding Path=ColumnName,Mode=TwoWay}" ToolTip="{Binding Path=ColumnName}" />
</DataTemplate>
<DataTemplate x:Key="ListBoxTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Key, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}" Width="80"/>
<ListBox ItemsSource="{Binding Path=Items}"
ItemTemplateSelector="{StaticResource myDataTemplateSelector}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.CanContentScroll="False" >
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal">
</StackPanel>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<ScrollViewer Grid.Row="0" VerticalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Visible">
<DockPanel>
<ListBox Name="headerListBox" MinHeight="20" DockPanel.Dock="Top"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.CanContentScroll="False"
ItemTemplate="{StaticResource HeaderTextBlockTemplate}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"></StackPanel>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
<ScrollViewer Name="listScrollViewer" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Visible">
<ListView Name="listView" SelectionMode="Extended"
VirtualizingStackPanel.IsVirtualizing="True"
ScrollViewer.IsDeferredScrollingEnabled="True"
VirtualizingStackPanel.VirtualizationMode="Recycling"
ItemsSource="{Binding}"
ItemTemplate="{StaticResource ListBoxTemplate}" >
</ListView>
</ScrollViewer>
</DockPanel>
</ScrollViewer>
<Button Grid.Row="1" Content="Button" Height="23" HorizontalAlignment="Left" Margin="10,10,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" />
</Grid>
</Window>
My dataProvider classes:
using System;
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections.Specialized;
using System.Windows.Data;
using System.Windows;
using DataVirtualization;
using System.Threading;
//using System.Threading;
namespace WpfApplication_async
{
public enum MySubKeyValueType { StringValue=0, ListValue=1 }
public class MySection : IItemsProvider<MyRecord>
{
/// <summary>
/// Fetches the total number of items available.
/// </summary>
/// <returns></returns>
public int FetchCount()
{
//Thread.Sleep(1000);
return Records.Count;
}
/// <summary>
/// Fetches a range of items.
/// </summary>
/// <param name="startIndex">The start index.</param>
/// <param name="count">The number of items to fetch.</param>
/// <returns></returns>
public IList<MyRecord> FetchRange(int startIndex, int count)
{
//Thread.Sleep(1000);
if (startIndex > Records.Count) startIndex = Records.Count;
if (startIndex + count > Records.Count) count = Records.Count - startIndex;
return Records.ToList().GetRange(startIndex, count).ToList();
}
public MySection()
{
Records = new ObservableCollection<MyRecord>();
}
public ObservableCollection<MyRecord> Records { get; set;}
}
public class MyRecord : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string info)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(info));
}
}
public MyRecord()
{
Items = new ObservableCollection<MySubKeyValue>();
}
private string key;
public string Key {
get {
return key;
}
set {
//if (Key!=null && this.Parent is MySection && (this.Parent as MySection).SectionDefinition.IsNumberedKeys)
// return;
key = value;
OnPropertyChanged("Key");
}
}
ObservableCollection<MySubKeyValue> items = new ObservableCollection<MySubKeyValue>();
public ObservableCollection<MySubKeyValue> Items
{
get {
return items;
}
set {
items = value;
OnPropertyChanged("NumberedColumnText");
}
}
}
public class MySubKeyValue : DependencyObject, INotifyPropertyChanged
{
private ColumnDefinition columnDef = null;
public ColumnDefinition ColumnDef
{
get
{
if (columnDef == null)
return columnDef = new ColumnDefinition();
return columnDef;
}
set { columnDef = value; }
}
public MySubKeyValue(string str = null)
{
Text = str;
ValueType = MySubKeyValueType.StringValue;
IsValidData = true;
ErrorMessage = "error";
}
private string text;
public MySubKeyValueType ValueType { get; set; }
public string Text
{
get
{
if (text == null) return String.Empty;
return text;
}
set
{
if (text != value)
{
text = value;
OnPropertyChanged("Text");
}
}
}
public bool isValidData = true;
public bool IsValidData
{
get { return isValidData; }
set
{
if (isValidData != value)
{
isValidData = value;
OnPropertyChanged("IsValidData");
}
}
}
public string ErrorMessage { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string info)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(info));
}
}
}
public class StringValue : MySubKeyValue
{
public StringValue(string str = null)
{
ValueType = MySubKeyValueType.StringValue;
Text = str;
}
}
public class ListValue : MySubKeyValue
{
private int index;
public int Index
{
get { return index; }
set
{
index = value;
if (index > -1 && index < valueEntityList.Count)
{
base.Text = valueEntityList[index];
IsValidData = true;
}
else
IsValidData = false;
OnPropertyChanged("Index");
}
}
public List<string> valueEntityList { get; set; }
public List<string> viewEntityList { get; set; }
public ListValue(string str, ListValue l)
{
ValueType = MySubKeyValueType.ListValue;
valueEntityList = l.valueEntityList;
viewEntityList = l.viewEntityList;
base.Text = str;
Index = valueEntityList.FindIndex(v => v == str);
}
public ListValue(List<string> _vals = null, List<string> _views = null, string str = "")
{
Index = -1;
ValueType = MySubKeyValueType.ListValue;
valueEntityList = new List<string>();
viewEntityList = new List<string>();
if (_vals != null)
if (_views != null && _views.Count == _vals.Count)
{
valueEntityList.AddRange(_vals);
viewEntityList.AddRange(_views);
}
else
{
valueEntityList.AddRange(_vals);
viewEntityList.AddRange(_vals);
}
base.Text = str;
Index = valueEntityList.FindIndex(v => v == str);
}
}
public class ColumnDefinition : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string info)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(info));
}
}
public ColumnDefinition(string name = "", int width = 80)
{
ColumnName = name;
ColumnWidth = width;
}
public string ColumnName { get; set; }
private int columnWidth;
public int ColumnWidth
{
get { return columnWidth; }
set { columnWidth = value; OnPropertyChanged("ColumnWidth"); }
}
}
}
MainWindow:
namespace WpfApplication_async
{
public partial class MainWindow : Window
{
MySection sec1,sec2;
bool isSec1 = true;
public MainWindow()
{
InitializeComponent();
sec1 = new MySection();
for(int i=0;i<50;i++){
MyRecord rec = new MyRecord();
rec.Key = i.ToString();
for(int j=0;j<20;j++){
rec.Items.Add(new StringValue("abc"));
rec.Items[rec.Items.Count - 1].ColumnDef = new ColumnDefinition(j.ToString());
}
sec1.Records.Add(rec);
}
sec2 = new MySection();
for (int i = 0; i < 50; i++)
{
MyRecord rec = new MyRecord();
rec.Key = i.ToString();
for (int j = 0; j < 20; j++)
{
rec.Items.Add(new ListValue(new List<string> { "a", "b" }, new List<string> { "a", "b" }, "a"));
rec.Items[rec.Items.Count - 1].ColumnDef = new ColumnDefinition(j.ToString());
}
sec2.Records.Add(rec);
}
}
private void button1_Click(object sender, RoutedEventArgs e)
{
if (isSec1)
//listView.DataContext = sec2;
listView.DataContext = new AsyncVirtualizingCollection<MyRecord>(sec2, 10, 30 * 1000);
else
//listView.DataContext = sec1;
listView.DataContext = new AsyncVirtualizingCollection<MyRecord>(sec1, 10, 30 * 1000);
isSec1 = !isSec1;
}
}
}
I found a simple solution. Instead of setting DataContext, I added items manually with low priority. UI does not freeze. Goal is achieved.
foreach (var r in sec.Records)
{
listView.Dispatcher.Invoke((new Action(delegate()
{
listView.Items.Add(r);
})), DispatcherPriority.SystemIdle);
}

Resources