Since my issue is rather uncommon and I have not a single clue what could be causing it, I will try to include every code that might have anything to do with it, stripped down to the very essentials to avoid unnecessary clutter.
Basically, I have a TabControl that includes TabItems whoes Contents are custom UserControls.
Those controls are of the following form:
<UserControl>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinition>
<ScrollViewer Height="Auto" HorizontalAlignment="Stretch" Margin="0" Name="scrollViewer11" VerticalAlignment="Stretch" Width="Auto" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"></ScrollViewer>
<ScrollViewer Grid.Column="1" Height="Auto" Name="scrollViewer12" Width="Auto" Margin="0" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto"></ScrollViewer>
<ScrollViewer Grid.Row="1" Height="Auto" Name="scrollViewer21" Width="Auto" Margin="0" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"></ScrollViewer>
<ScrollViewer Grid.Row="1" Grid.Column="1" Height="Auto" Name="scrollViewer22" Width="Auto" Margin="0" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"></ScrollViewer>
</Grid>
</UserControl>
The amount of rows and columns depends on the control used and ranges from a single cell in a 1x1 grid to 6 cells in a 3x2 grid (each contraining the ScrollViewer).
Now to my actual C# code. To each cell the user may add a WriteableBitmap where the code is organized as such:
public partial class MainWindow : Window
{
List<WorkSpace> WorkSpaceList = new List<WorkSpace>();
private WorkSpace currentSpace = null;
//one of the methods that adds to the TabControl, here a 2x2 grid:
private void NewWorkspaceFour(WorkSpace ws)
{
WorkSpaceFour workSpaceFour = new WorkSpaceFour();
for (int i = 0; i < 4; i++)
{
WorkPlace wp = new WorkPlace();
ws.workPlaceList.Add(wp);
switch (i)
{
case 0:
workSpaceFour.scrollViewer11.Content = wp.grid;
break;
case 1:
workSpaceFour.scrollViewer12.Content = wp.grid;
break;
case 2:
workSpaceFour.scrollViewer21.Content = wp.grid;
break;
case 3:
workSpaceFour.scrollViewer22.Content = wp.grid;
break;
}
}
ws.tabItem.Content = workSpace;
tabControlImages.Items.Add(ws.tabItem);
}
//triggered in UI e.g. by moving a Slider
private void NewSettings(object sender, RoutedPropertyChangedEventArgs<double> e)
{
currentSpace.NewSettings((float)(e.NewValue));
}
}
internal class WorkSpace
{
internal delegate void NewSettingHandler(float e);
internal List<WorkPlace> workPlaceList = new List<WorkPlace>();
internal TabItem tabItem = new TabItem();
internal WorkPlace currentPlace = null;
internal NewSettingsHandler NewSettings;
internal WorkSpace()
{
NewSettings += ChangeSettings;
}
internal void ChangeSettings(float newValue)
{
//do something small with newValue
currentPlace.RecalculateImage();
}
//...more methods that would use "newValue" in a different way, thus the delegate
}
internal class WorkPlace
{
internal WriteableBitmap bitmap;
internal Image image = new Image {//settings};
internal Grid grid = new Grid {//settings};
internal Grid gridImage = new Grid {//settings};
internal WorkPlace()
{
grid.Children.Add(gridImage);
gridImage.Children.Add(image);
}
internal void RecalculateImage()
{
//some rather long calculations...
bitmap.WritePixels(...,...,...,...);
image.Source = bitmap;
}
}
Through the program the user can change the tabs to change currentSpace and click on the cells to change the respective currentPlace both simply by change the reference i.e. currentSpace = space, where space refers to the WorkSpace that contains the new selected TabItem.
Now the issue is as follows. When a tab contains a simple 1x1 grid with an image in it and I move the slider, it runs very smoothly. When a tab contains a 3x2 grid and only a single cell of this grid contains a WorkPlace with a bitmap that is not null it works the same. However, when the 3x2 grid is completely filled with "painted" bitmaps and the slider is moved a noticable lag appears, even though only a single of the 6 images is recalculated redrawn. I don't see why this should be the case. It might have something to do with rendering or the object references but I don't know C# well enough to see and issues here.
Hopefully, the code is not too long (I have stripped it down as much as I could) and clear enough. If anything is not, please say so and I will update/add it. In a previous version of this program the UI was basically the same but the recalculation of the images was fairly different but I could never observe this problem.
Related
I'm trying to create something looking like this :
It's designed to be an XAML title for VMIX software, video broadcasting purposes.
I'm gonna get a lot of datas from a GSheet, handle in VMIX, and assign those datas to my TextBlocks such as "Candidate", "City" and the Votes %.
From that % I want the bar size to increase/decrease, I managed to do part of that.
But the main issue is to get the % TextBlock margin to fit on the right of the rectangle.
Anyone knows how I could do that ?
I have never been coding in C#, I have a background in C, C++ and JS, so I've spent my day looking for that purpose and couldn't make it right.
I saw some binding methods that could fit, but I'm unable to use them.
Moreover I'm working on Blend for Visual Studio 2017, and I don't get why I can't run some simple code on it when pressing F5... It's another problem thought.
Thanks a lot for your help.
EDIT :
I've reached something new so far, really DIY solution but it's my lsat solution if I can't find better :
I'll have 2 TextBlock for 1 ProgressBar (Thanks to Chris)
<Grid Margin="0,0,-8,0">
<TextBlock x:Name="Votes1" HorizontalAlignment="Left" TextWrapping="Wrap" VerticalAlignment="Top" Margin="{Binding Text, ElementName=MarginVotes1}" FontSize="72" Width="853" Height="188"><Run Text="6"/><Run Text="00"/></TextBlock>
<ProgressBar HorizontalAlignment="Left" Height="79" Margin="171,503,0,0" VerticalAlignment="Top" Width="{Binding Path=Text, ElementName=Votes1}" Background="#FFEA4545"/>
<TextBlock x:Name="MarginVotes1" HorizontalAlignment="Left" Margin="171,587,0,0" TextWrapping="Wrap" VerticalAlignment="Top" FontSize="72" Height="98" Width="550"><Run Text="8"/><Run Text="0"/><Run Text="0"/><Run Text=","/><Run Text="4"/><Run Text="9"/><Run Text="0"/><Run Text=",0,0"/>
</TextBlock>
So this works fine, but I have to prepare before what my "MarginVotes1" value is (in GoogleSheet).
The best would be directly in code behind to do something like this :
CONVERT Votes1.Text to Int STORE in val
SET x to val + DefaultMargin
CONVERT x to String STORE in MarginX
CREATE String MarginVoteStr as MarginX + ",500, 0, 0"
SET Votes1.Margin as MarginVoteStr
Welcome to WPF. Here's some code I put together that you should be pretty close to what you need.
XAML:
<ItemsControl Grid.IsSharedSizeScope="True" ItemsSource="{Binding Candidates}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Left">
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="Candidate" Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Name}"/>
<Rectangle Grid.Column="1" Height="10" Margin="5, 0" Width="{Binding BarWidth}" Fill="{Binding BarColor}"/>
<TextBlock Grid.Column="2" Text="{Binding Percentage, StringFormat=P}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
C#:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Candidates = new List<Candidate> { new Candidate { Name = "Joe", Percentage = .50, BarColor = Brushes.Green},
new Candidate { Name = "Bob", Percentage = .30, BarColor = Brushes.Yellow},
new Candidate { Name = "Sarah", Percentage = .20, BarColor = Brushes.Gray}};
}
public List<Candidate> Candidates
{
get { return (List<Candidate>)GetValue(CandidatesProperty); }
set { SetValue(CandidatesProperty, value); }
}
public static readonly DependencyProperty CandidatesProperty =
DependencyProperty.Register("Candidates", typeof(List<Candidate>), typeof(MainWindow));
}
public class Candidate
{
public string Name { get; set; }
public double Percentage { get; set; }
public Brush BarColor { get; set; }
//This is just shorter syntax for a readonly property.
//The multiplier (200) should be whatever length you want a full bar to be
public double BarWidth => Percentage * 200;
}
There are a number of points you should note:
ItemsControl and DataTemplate
Whenever you need to display multiple data items in WPF, especially if the number of items is variable, you should be using some type of ItemsControl.
An ItemsControl takes a collection of some kind and displays each item using a DataTemplate. An ItemsControl creates a new instance of its ItemTemplate for every item in its source collection. The link between the data and the visuals is established through data bindings.
Layout
Everything between the <DataTemplate> tags is the visual layout of a single item.
Notice that I am not using Margin to create the desired layout. Instead of using Margin in that way, I'm using one of WPFs many Panel controls: Grid. With Grid you can define rows and columns like a table.
Each item in my example is a Grid with 1 row and 3 columns. The elements that make up the item are placed in that grid using the Grid.Column property. Each column has Width="Auto", which means it will grow to accommodate the width of what's inside. IsSharedSizeScope and SharedSizeGroup make it so that the Grids of each individual item all have the same width for the first column.
Candidate class
This is the class that will be used to store and represent the data being displayed. Note that the property names match the {Binding ______} values from the DataTemplate.
My example main window has a collection of Candidate objects stored in a dependency property. This property is bound to the ItemsSource of the ItemsControl.
Overall
The idea is to populate your collection with whatever data items you need and let the ItemsControl take care of the rest, thus keeping the data and visuals of the project relatively independent. Even small visual aspects like formatting the percentage value correctly for display can be done using the DataTemplate instead of writing the code in C#, as shown using StringFormat=P.
I have a Universal Windows Application for a local bank, I'm working on a money transfer view, and they need to transfer money from account to account using the Drag and Drop feature in UWP applications.
I've made the animation part, but I need help after I drop the list item to the "Account To" list.
I'll attach a screenshot to make it clear.
As you see in the picture, I need to drag one item from the "From Account" list and drop it on only one item on "To Account" list. How can I achieve this ?
I've created a small sample which shows drag-drop between two ListViews filled with some Accounts. I will skip the implementation of UserControls - the Page xaml looks like this:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="200"/>
<RowDefinition Height="200"/>
</Grid.RowDefinitions>
<ListView Header="Source" Margin="10" Grid.Row="0" CanDragItems="True" ItemsSource="{x:Bind Accounts}" SelectionMode="None">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsStackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<controls:AccountControl CanDrag="True" DragStarting="AccountControl_DragStarting"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<ListView Header="Targets" Margin="10" Grid.Row="1" ItemsSource="{x:Bind Accounts}" SelectionMode="None">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsStackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<controls:AccountControl AllowDrop="True" DragEnter="AccountControl_DragEnter" Drop="AccountControl_Drop"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
As you can see there is a Source list in which the control is firing an event when it's being dragged.
private void AccountControl_DragStarting(UIElement sender, DragStartingEventArgs args)
{
if ((sender as AccountControl)?.DataContext is Account account)
{
args.AllowedOperations = DataPackageOperation.Link;
args.Data.SetData(accountId, account.Id);
}
}
The Account class apart from name and balance has a Guid identifier so I can use it to pass information which source account has been used in transfer method.
The items in second list (Targets) accepts only drop operation and for this purpose fire two events:
private void AccountControl_DragEnter(object sender, DragEventArgs e)
{
e.AcceptedOperation = DataPackageOperation.Link;
e.DragUIOverride.Caption = "Transfer";
}
private async void AccountControl_Drop(object sender, DragEventArgs e)
{
if ((e.OriginalSource as AccountControl)?.DataContext is Account targetAccount)
if (await (e.DataView.GetDataAsync(accountId)) is Guid sourceAccountId)
{
var sourceAccount = Accounts.First(x => x.Id == sourceAccountId);
sourceAccount.Balance -= 1000;
targetAccount.Balance += 1000;
}
}
The first one sets accepted operation and some information for the user. The second one 'transfers' some money from one account to the second.
Everything looks like this:
Some more help you can find at MS directly, other article and in MS samples repository.
I am not fully satisfied with the "solutions" which I will provide. They are much likely very far away from the ideal implementations, but ...
The XAML code which I created to try to replicate as easily, but also consistently your object, consisted in a group of draggable Rectangles inside a StackPanel Control, plus another StackPanel Control where the items could be dragged into.
<Grid>
<Grid.Resources>
<Style TargetType="Rectangle">
<Setter Property="Width" Value="300"/>
<Setter Property="Height" Value="300"/>
<Setter Property="CanDrag" Value="True"/>
</Style>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Name="StackPanelRectangles" Grid.Row="0" Orientation="Horizontal">
<Rectangle x:Name="RedRect" Fill="Red" DragStarting="Rectangle_DragStarting"/>
<Rectangle x:Name="GreenRect" Fill="Green" DragStarting="Rectangle_DragStarting"/>
<Rectangle x:Name="BlueRect" Fill="Blue" DragStarting="Rectangle_DragStarting"/>
</StackPanel>
<StackPanel Name="StackPanelDropArea" Background="Azure" AllowDrop="True"
DragOver="StackPanel_DragOver" Drop="StackPanel_Drop"
Grid.Row="2" Orientation="Horizontal"
HorizontalAlignment="Center">
<TextBlock>Drop anywhere in this area area</TextBlock>
</StackPanel>
</Grid>
1st Solution:
I routed every DragStarting event of the multiple Rectangles to the same EventHandler. In this EventHandler, we have access to the UIElement which is being dragged, so with an exposed property of type UIElement in your Page class, and you can simply clone the necessary properties for when you need to drop it, like this:
UIElement dragItem;
private void Rectangle_DragStarting(UIElement sender, DragStartingEventArgs args)
{
dataPackage.RequestedOperation = DataPackageOperation.Copy;
dragItem = sender;
}
Then when the item is dropped EventHandler is called, I have simply add it onto my DropArea.
private void StackPanel_Drop(object sender, DragEventArgs e)
{
Rectangle newElement = new Rectangle();
newElement.Width = (dragItem as Rectangle).Width;
newElement.Height = (dragItem as Rectangle).Height;
newElement.Fill = (dragItem as Rectangle).Fill;
StackPanelDropArea.Children.Add(newElement);
}
You cannot add your new Control by setting to reference the object being dragged, since there are properties such as the respective Parent which will thrown an exception when you try to add the Control to a different container.
2nd Solution:
I was extremely focused on on utilizing the DataPackage object, and one of its supported default formats, but I don't think any of them can actually hold data of an Object, such as our UIElement.
But each DataPackage instance supports a set of properties, which corresponds a Dictionary. We can set the Dictionary to hold UIElement in there, as long as we specify a key to reference that same object later on.
private void Rectangle_DragStarting(UIElement sender, DragStartingEventArgs args)
{
dataPackage.RequestedOperation = DataPackageOperation.Copy;
args.Data.Properties.Add("myRectangle", sender);
}
In the drop Event Handler, you can obtain the UIElement, like such:
private async void StackPanel_Drop(object sender, DragEventArgs e)
{
Rectangle element = e.DataView.Properties["myRectangle"] as Rectangle;
......
......
}
3rd Solution:
This solution used the method SetText(String) exposed by DataPackage, to hold the value of the Name property of the UIElement being dragged.
private void Rectangle_DragStarting(UIElement sender, DragStartingEventArgs args)
{
dataPackage = new DataPackage();
dataPackage.RequestedOperation = DataPackageOperation.Copy;
Rectangle rectangle = sender as Rectangle;
dataPackage.SetText(rectangle.Name);
Clipboard.SetContent(dataPackage);
}
By knowing the value of the Name property of the UIElement which is being dragged, looked for it, by using the VisualTreeHelper Class, like this:
private async void StackPanel_Drop(object sender, DragEventArgs e)
{
DataPackageView dataPackageView = Clipboard.GetContent();
if (dataPackageView.Contains(StandardDataFormats.Text))
{
draggedObject = await dataPackageView.GetTextAsync();
}
// Dragged objects come from another one of our Parent's Children
DependencyObject parent = VisualTreeHelper.GetParent(StackPanelDropArea);
int count = VisualTreeHelper.GetChildrenCount(parent);
for(int i=0; i< count; i++)
{
DependencyObject child = VisualTreeHelper.GetChild(parent, i);
if(child.GetType().Equals(typeof(StackPanel)))
{
StackPanel currentStackPanel = child as StackPanel;
if(currentStackPanel.Name == "StackPanelRectangles")
{
int numberOfRectangles = VisualTreeHelper.GetChildrenCount(currentStackPanel);
for(int j=0; j<numberOfRectangles; j++)
{
if(VisualTreeHelper.GetChild(currentStackPanel,j).GetType().Equals(typeof(Rectangle)))
{
Rectangle currentRectangle = VisualTreeHelper.GetChild(currentStackPanel, j) as Rectangle;
if (draggedObject != string.Empty && currentRectangle.Name.Equals(draggedObject))
{
Rectangle newRectangle = new Rectangle();
newRectangle.Width = currentRectangle.Width;
newRectangle.Height = currentRectangle.Height;
newRectangle.Fill = currentRectangle.Fill;
StackPanelDropArea.Children.Add(newRectangle);
}
}
}
}
}
} */
}
Result:
I usually try tackling this several times before giving up and using a third party library. The one I typically use is:
https://github.com/punker76/gong-wpf-dragdrop
You may subscribe to PointerPressed event in your DataTemplate and extract all the things you need.
XAML:
<DataTemplate x:Name="DataTemplate">
<Grid Background="Transparent" PointerPressed="Grid_OnPointerPressed"/>
</DataTemplate>
Code:
private void Grid_OnPointerPressed(object sender, PointerRoutedEventArgs e)
{
//your FrameworkElement
var frameworkElement = sender as FrameworkElement;
//global position of your element
var itemPosition = frameworkElement.TransformToVisual(Window.Current.Content).TransformPoint(new Point(0, 0)).ToVector2();
//your data
var selectedItemData = frameworkElement.DataContext as ItemData;
}
Save your data, use UWP Drag'n'Drop. On drop load your data.
I want to layout my items in a Windows Phone 8.1 app, not silverlight, in the following order:
I did some research and tried different panels, but I can't find the right ones :[
I could use a grid and achive that design, BUT I want to add items over a binding and then I would have to change the grid somehow :/
xaml Layout
<Page.DataContext>
<uc:Test/>
</Page.DataContext>
<ScrollViewer>
<ItemsControl ItemsSource="{Binding t}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Aqua"
BorderThickness="3"
Width="100" Height="100">
<TextBlock Text="{Binding}"/>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
test.cs
public class Test
{
public ObservableCollection<string> t { get; set; }
public Test()
{
t = new ObservableCollection<string>();
t.Add("a");
t.Add("b");
t.Add("c");
t.Add("d");
t.Add("e");
}
}
Edit:
ALSO, I did write a wrong information in the comment below, sorry.
Every Item has the same width, so count and width, will/would give me the position in column and row.
Implementation of PrepareContainerForItemOverride so far:
public class ExtendedItemsControl : ItemsControl
{
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
base.PrepareContainerForItemOverride(element, item);
var grid = element as ContentPresenter;
var count = 0; // <- Count of Items in the Grid
var width = 0; // <- width of the current Element
//if (count * width / grid.ActualWidth > 1)
// grid.RowDefinitions.Add(new RowDefinition());
Grid.SetRow(grid, 0);
}
}
You can use a Grid along with an ItemsControl to achieve the ItemsSource binding:
First, set the Grid as the ItemsControl's ItemsPanel
Second, subclass the ItemsControl to set the appropriate Grid.Row and Grid.Column properties on its children
For the first part (it looks from the picture like you have 4 columns and 3 rows):
<local:ExtendedItemsControl ItemsSource="{Binding MyItems}">
<local:ExtendedItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
</Grid>
</ItemsPanelTemplate>
</local:ExtendedItemsControl.ItemsPanel>
</local:ExtendedItemsControl>
For the second part, I suggest overriding OnItemsChanged, and setting the Grid attached properties on each item container as needed. You could do this by using the implicit sequence of the items:
public class ExtendedItemsControl : ItemsControl
{
protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
{
base.OnItemsChanged(e);
var item2 = this.ItemContainerGenerator.ContainerFromItem(e.NewItems[1]);
Grid.SetColumn(item2, 1);
var item3 = this.ItemContainerGenerator.ContainerFromItem(e.NewItems[2]);
Grid.SetColumn(item3, 2);
Grid.SetColumnSpan(item3, 2);
var item4 = this.ItemContainerGenerator.ContainerFromItem(e.NewItems[3]);
Grid.SetRow(item4, 1);
// etc ...
}
}
The above assumes that your source collection doesn't not change once bound -- if it does change, you might consider overriding PrepareContainerForItemOverride instead, and setting its Grid Row/Column properties with reference to a property on the item model ("ItemIndex" or whatever):
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
base.PrepareContainerForItemOverride(element, item);
var contentPresenter = (ContentPresenter)element;
var itemModel = (MyItemModel)item;
switch (itemModel.ItemIndex)
{
case 1:
Grid.SetColumn(contentPresenter, 1);
break;
case 2:
Grid.SetColumn(contentPresenter, 2);
Grid.SetColumnSpan(contentPresenter, 2);
break;
// etc
}
}
There isn't a standard control that will give you the layout you want for arbitrary numbers of different sized items without some custom placement code, but you can customize controls depending on what exactly you need.
Mark Rideout created a customized GridView sample for Windows Store 8.0 at How To: Create a Variable Sized Grouped GridView (like the store) and the techniques you'll use for a Windows Phone Runtime app will be essentially the same. In his control he overrode the PrepareContainerForItemOverride function to look at the individual data items to see if they should be small, medium, or large sized, and then set their columns and spans appropriately in a VariableSizedWrapGrid.
If you want the exact positioning you show (rather than lining things up) and want to limit to 7 then you could set the ItemsPanel to a Grid instead of the VariableSizedWrapGrid and set the items into specific rows and columns in the same way.
I am working on windows phone 8 app.
I have List box with over 200 items to display.
<DataTemplate x:Key="DataTemplate1">
<Grid VerticalAlignment="Center" HorizontalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<Border Grid.Row="0" Background="White" Height="400" Width="400" CornerRadius="30,30,30,30">
</Border>
<Grid Grid.Row="0" HorizontalAlignment="Center" VerticalAlignment="Top">
<TextBlock HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="5,20,5,5"
Foreground="#000000"
Text="{Binding Title}"/>
</Grid>
</Grid>
</DataTemplate>
But it crashes, i have debugged it till 100 items it works but after that it crashes.
In the PhoneApplicationPage_Loaded method i have
private void PhoneApplicationPage_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
myList.Add(new MyObject("A","A value"));
myList.Add(new MyObject("B", "B value"));
myList.Add(new MyObject("C", "C value"));
and so on... 200 items
ListBoxItems.ItemsSource = myList;
}
how can i fix this ?
Update :
<ItemsPanelTemplate x:Key="ItemsPanelTemplate">
<local:CollectionFlowPanel ItemHeight="400"
ItemWidth="400"
FocusedItemOffset="120"
UnfocusedItemOffset="20"
ItemVisibility="5">
<VirtualizingStackPanel />
</local:CollectionFlowPanel>
</ItemsPanelTemplate>
</phone:PhoneApplicationPage.Resources>
<Grid x:Name="LayoutRoot" Background="#000000">
<local:CollectionFlow x:Name="ListBoxItems"
ItemTemplate="{StaticResource DataTemplate}"
ItemsPanel="{StaticResource ItemsPanelTemplate}"/>
</Grid>
Ensure you have VirtualizingStackPanel inside the ItemsPanelTemplate of your list box, see this answer for more info.
Here's the XAML you likely need for your ListBox:
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
You need to read following blog from msdn on visualization of the data in list and grid.
Using virtualization with a list or grid
Without seeing your whole xaml code I cannot suggest the exact answer but my guess is that you in xaml ListBox is placed inside a canvas/StackPanel or scrollviewer control.
When the size of the ItemsControl's viewport isn't restricted, the control doesn't perform virtualization. Instead, it creates an item container for each item in its collection. Some common containers that don't restrict the viewport size are Canvas, StackPanel, and ScrollViewer. You can enable virtualization in this situation by setting the size of ItemsControl directly, instead of letting it be sized by its parent container.
Here, we set the Height and Width on the GridView. This restricts the size of the viewport, and items outside of the viewport are virtualized.
Below are 2 scenarios one will throw out of memory exception and other will work fine(use your same code behind and test)
1. ListBox in Canvas
<Canvas .....
<ListBox Name="ListBoxItems".....
</ListBox>
</Canvas>
Above code will throw out of memory exception as items control's viewport is not defined (if you still want to use Canvas than define width/height if ListBox in that case the port of Items control is defined and virtulazation will apply)
2. ListBox in Grid
<Grid .....
<ListBox Name="ListBoxItems".....
</ListBox>
</Grid>
The above code will not throw out of memory exception as virtuallization is applied on the listbox.
Hope this will help
How big is your object ? If your object is too big you might not be able to load them all at once.
Did you try using the for loop?
public List<Fellow> fellowList { get; set; }
private void PhoneApplicationPage_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
fellowList = new List<Fellow>();
for (int i = 0; i < 2; i++)
{
Fellow fellow = new Fellow();
fellow.x = "B" + i;
fellow.value = "B Value" + i;
fellowList.Add(fellow);
}
this.DataContext = this;
ListBoxItems.ItemsSource = fellowList;
}
public class Fellow
{
public string x { get; set; }
public string value { get; set; }
}
Hope it helps..change the view model according to your wish
I'd like some tips-in-the-right-direction or even ready solutions to this problem and I'm pretty stuck (I'm just beginner/intermediate):
I'm trying to implement a SSH in my application. The SSH-backend works fine and such, but I'm stuck at the frontend. What WPF-Combination would present me with an adequate solution to emulate a console? Put aside a complete terminal-emulation, I'd be happy to simply readline/writeline into something that looks like a console :-)
My best approach yet was a 80x50 Grid of single characters resulting in 4000 single cells and that feels like a total overkill.
Another idea was to make a console-Appl. bound to a wpf-window in another project. But...is that even possible and how?
Given that you want to emulate a console, I'd do it like this. Note that you'd have to handle the commands and outputting the results yourself.
page.xaml
<Window x:Class="ConsoleEmulation.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" MinHeight="350" MinWidth="525" Height="350" Width="525">
<Grid>
<ScrollViewer Name="Scroller" Margin="0" Background="Black">
<StackPanel>
<ItemsControl ItemsSource="{Binding ConsoleOutput, Mode=OneWay}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=.}" Foreground="White" FontFamily="Consolas"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<TextBox Text="{Binding ConsoleInput, Mode=TwoWay}" Background="Black" Foreground="White" FontFamily="Consolas" Name="InputBlock" BorderBrush="{x:Null}" SelectionBrush="{x:Null}" />
</StackPanel>
</ScrollViewer>
</Grid>
</Window>
page.xaml.cs
public partial class MainWindow : Window
{
ConsoleContent dc = new ConsoleContent();
public MainWindow()
{
InitializeComponent();
DataContext = dc;
Loaded += MainWindow_Loaded;
}
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
InputBlock.KeyDown += InputBlock_KeyDown;
InputBlock.Focus();
}
void InputBlock_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
dc.ConsoleInput = InputBlock.Text;
dc.RunCommand();
InputBlock.Focus();
Scroller.ScrollToBottom();
}
}
}
public class ConsoleContent : INotifyPropertyChanged
{
string consoleInput = string.Empty;
ObservableCollection<string> consoleOutput = new ObservableCollection<string>() { "Console Emulation Sample..." };
public string ConsoleInput
{
get
{
return consoleInput;
}
set
{
consoleInput = value;
OnPropertyChanged("ConsoleInput");
}
}
public ObservableCollection<string> ConsoleOutput
{
get
{
return consoleOutput;
}
set
{
consoleOutput = value;
OnPropertyChanged("ConsoleOutput");
}
}
public void RunCommand()
{
ConsoleOutput.Add(ConsoleInput);
// do your stuff here.
ConsoleInput = String.Empty;
}
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(string propertyName)
{
if (null != PropertyChanged)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Did you know that you can display a Console window from your application by using AllocConsole?
This is a simple way to create a "dual-mode" application can be a
console or windows forms application.
[DllImport("kernel32")]
static extern bool AllocConsole();
Or you can use this:
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<TextBlock Text="Console contents..." HorizontalAlignment="Stretch" VerticalAlignment="Stretch" x:Name="ConsoleTextBlock"/>
<DockPanel Grid.Row="1">
<TextBox/>
</DockPanel>
</Grid>
For better looks, replace the TextBlock with a ListBox and style the ItemTemplate accordingly.
I haven't done it myself, however it is one of my "I'll do it if I have time"-projects.
Thus I am still looking for an existing implementation :-P
Anyways some thoughts:
The applroach to use Visuals (i.e. Ellipses, Textblocks) is probably not a good Idea.
Just think of what has to happen if you want like 200x100 characters. Maybe even a backbuffer. Holding it all in memory + drawing it....it will be incredibly slow.
Therefore the better (or even right) approach is to "draw yourself". Since WPF is backbuffered and you don't want to display an arbitrary bitmap the most likly approach would be to create a new UserControl and override it's Paint-Method.
You ma prefer to derive from Control, but UserControl may have Content, so you can show something like a connection indicator icon inside.
Architecture-wise I'd suggest to create a dependecy property Buffer (ConsoleBuffer) that holds the console buffer-model. Another DP would hold the top-left positon Location (long). It determines where to start the display (while you have a look behind). The console model I would make a class that contains a char[] and a Color[] (one dimensional). Use line breaking and \n characters to make lines (because this is the character of a console). Then if you resize the control it will re-flow without the buffer needing to be re-allocated.
You can work with **ConsoleBuffer**s of different sizes (for a different number of look behind characters).
ConsoleBuffer.Write(string s) is your method to do stuff.
Maybe it is advisable to hold arrays of arrays char[][] to represent lines.... but that is up to finding out while programming.