See the following code,
After I clicked the button, the listbox render several times.
How can I prevent Listbox flickering?
Is it possible to tell a control stop update/render?
<UserControl x:Class="SilverlightApplication52.MainPage"
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"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<Grid x:Name="LayoutRoot"
Background="Gray"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<ListBox x:Name="listbox"
Background="White"
Margin="100">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Rectangle Width="{Binding Width}"
Height="{Binding Height}"
Fill="{Binding Background}"
RenderTransformOrigin="0.5,0.5">
<Rectangle.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="{Binding Scale}"
ScaleY="{Binding Scale}" />
<RotateTransform Angle="{Binding Angle}" />
<TranslateTransform X="{Binding Left}"
Y="{Binding Top}" />
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Content="test"
Width="50"
Height="50"
Click="Button_Click" />
</Grid>
public partial class MainPage : UserControl
{
public class ItemInfo
{
public double Left { get; set; }
public double Top { get; set; }
public double Width { get; set; }
public double Height { get; set; }
public double Angle { get; set; }
public double Scale { get; set; }
public Brush Background { get; set; }
}
ObservableCollection<ItemInfo> _items = new ObservableCollection<ItemInfo>();
public MainPage()
{
InitializeComponent();
listbox.ItemsSource = _items;
}
Random random = new Random();
private void Button_Click(object sender, RoutedEventArgs e)
{
_items.Clear();
for (int i = 0; i < 2000; i++)
{
byte r = (byte)(random.NextDouble()*255);
byte g = (byte)(random.NextDouble()*255);
byte b = (byte)(random.NextDouble()*255);
_items.Add(
new ItemInfo
{
Left = random.NextDouble() * 500,
Top = random.NextDouble() * 500,
Width = random.NextDouble() * 1000,
Height = random.NextDouble() * 1000,
Angle = random.NextDouble() * 359,
Scale = random.NextDouble() * 1,
Background = new SolidColorBrush(Color.FromArgb(255,r,g,b)),
}
);
}
}
}
Try adding to a separate ObservableCollection in that loop (that is not referenced/bound to the listbox). Then when the loop is done assign the listbox ItemsSource to the new observablecollection.
Related
My apologies for such a basic question, if it is.
The issue is I am supposed to draw two parallel lines or two parallel curves on a canvas. I want to set a color between those two non-intersecting lines. I am using two Polylines to draw them.
Any help is appreciated. Thanks in advance.
Code:
<Canvas.LayoutTransform>
<ScaleTransform CenterX="0" CenterY="0" ScaleY="-1" ScaleX="1"/>
</Canvas.LayoutTransform>
<Polyline Name="MyLine1" Points="{Binding BindPoints1,Mode=TwoWay}" Stroke="Black" StrokeThickness="4" Grid.Row="0" />
<Polyline Name="MyLine2" Points="{Binding BindPoints2,Mode=TwoWay}" Stroke="Black" StrokeThickness="4" Grid.Row="0" />
And C#
public class ViewModel : ViewModelBase
{
private ImageSource m_CreatedImage;
public PointCollection BindPoints1 { get; set; }
public PointCollection BindPoints2 { get; set; }
public ViewModel()
{
BindPoints1 = new PointCollection();
BindPoints2 = new PointCollection();
for (int i = 0; i < 1000; i++)
{
double val = (i * i) - 5;
var point = new Point(i, i+20);
BindPoints1.Add(point);
}
BindPoints2 = new PointCollection();
for (int i = 0; i < 1000; i++)
{
double val = (i * i) + 5;
var point = new Point(i, i-20);
BindPoints2.Add(point);
}
}
}
The best thing is to define a grid and divide it into 4-5 rows first.
In the first and last row add the line. Span the middle 2-3 rows and add a shape there say rectangle or ellipse as per your requirement, and just fill it with the required color.
Check the sample below.
<Window x:Class="WpfApplication1.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:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525"
>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Polyline Name="MyLine1" Grid.Row="0" Points="{Binding BindPoints1,Mode=TwoWay}" Stroke="Black" StrokeThickness="4" />
<Polyline Name="MyLine2" Grid.Row="4" Points="{Binding BindPoints2,Mode=TwoWay}" Stroke="Black" StrokeThickness="4" />
<Rectangle Grid.Row="1" Grid.RowSpan="3" Fill="Red" />
</Grid>
</Window>
Create a third point collection that contains all the points of the first line and all the points of the second line. The points in the second line need to be reversed. Think of it like walking to the end of a street down one side, crossing, then returning down the other side.
Bind a new Line to this third set of points and set Fill instead of the Stroke and draw it before drawing your other lines/arcs.
<Polyline Name="FillLine" Points="{Binding BindPoints3,Mode=TwoWay}" Fill="Green" Grid.Row="0"/>
<Polyline Name="MyLine1" Points="{Binding BindPoints1,Mode=TwoWay}" Stroke="Black" StrokeThickness="4" Grid.Row="0" />
<Polyline Name="MyLine2" Points="{Binding BindPoints2,Mode=TwoWay}" Stroke="Black" StrokeThickness="4" Grid.Row="0" />
view model:
public class ViewModel : ViewModelBase
{
private ImageSource m_CreatedImage;
public PointCollection BindPoints1 { get; set; }
public PointCollection BindPoints2 { get; set; }
public PointCollection BindPoints3 { get; set; }
public ViewModel()
{
BindPoints1 = new PointCollection();
BindPoints2 = new PointCollection();
for (int i = 0; i < 1000; i++)
{
double val = (i * i) - 5;
var point = new Point(i, i + 20);
BindPoints1.Add(point);
}
BindPoints2 = new PointCollection();
for (int i = 0; i < 1000; i++)
{
double val = (i * i) + 5;
var point = new Point(i, i - 20);
BindPoints2.Add(point);
}
BindPoints3 = new PointCollection(BindPoints1.OfType<Point>().Concat(BindPoints2.OfType<Point>().Reverse()));
}
}
I needed to invert Columns/Rows on my DataGrid (see WPF horizontal DataGrid and RotatedDataGrid)
Once I inverted it, I am having some weird effects on the images displayed inside my datagrid.
When I scroll down, the column 1 will crop the image by the left and a little on the right. The more I go down, the more it crops the left until there is nothing more.
How can I solve that ?
Here a full simple example if you want to test it (you just need to copy/paste it in a new project and scroll down to see the problem)
MainWindows.xaml
<Window x:Class="RotatedDataGrid.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="150" Width="1000">
<Grid>
<DataGrid x:Name="MyRotatedDataGrid" HorizontalContentAlignment="Center"
ScrollViewer.HorizontalScrollBarVisibility="Visible" ScrollViewer.VerticalScrollBarVisibility="Visible"
AutoGenerateColumns="True"
ItemsSource="{Binding Customers}">
<DataGrid.Resources>
<Style x:Key="DataGridBase" TargetType="Control">
<Setter Property="LayoutTransform">
<Setter.Value>
<TransformGroup>
<RotateTransform Angle="-90" />
<ScaleTransform ScaleX="1" ScaleY="-1" />
</TransformGroup>
</Setter.Value>
</Setter>
<Setter Property="TextOptions.TextFormattingMode" Value="Display" />
</Style >
<Style TargetType="DataGridCell" BasedOn="{StaticResource DataGridBase}"/>
<Style TargetType="DataGridColumnHeader" BasedOn="{StaticResource DataGridBase}"/>
<Style TargetType="DataGridRowHeader" BasedOn="{StaticResource DataGridBase}"/>
</DataGrid.Resources>
<DataGrid.LayoutTransform>
<TransformGroup>
<RotateTransform Angle="90" />
<MatrixTransform Matrix="-1, 0, 0, 1, 0, 0" />
</TransformGroup>
</DataGrid.LayoutTransform>
<DataGrid.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=Name}" FontWeight="Bold" Padding="3"/>
</StackPanel>
</DataTemplate>
</GroupStyle.HeaderTemplate>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander>
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Name}"/>
<TextBlock Text="{Binding Path=ItemCount}" Margin="8,0,4,0"/>
<TextBlock Text="Items"/>
</StackPanel>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</DataGrid.GroupStyle>
</DataGrid>
</Grid>
</Window>
MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace RotatedDataGrid
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public ICollectionView Customers { get; private set; }
public ICollectionView GroupedCustomers { get; private set; }
public MainWindow()
{
InitializeComponent();
var _customers = new List<Customer>
{
new Customer
{
FirstName = "Christian",
LastName = "Moser",
Gender = Gender.Male,
WebSite = new Uri("http://www.wpftutorial.net"),
ReceiveNewsletter = true,
Image = "Images/christian.jpg"
},
new Customer
{
FirstName = "Peter",
LastName = "Meyer",
Gender = Gender.Male,
WebSite = new Uri("http://www.petermeyer.com"),
Image = "Images/peter.jpg"
},
new Customer
{
FirstName = "Lisa",
LastName = "Simpson",
Gender = Gender.Female,
WebSite = new Uri("http://www.thesimpsons.com"),
Image = "Images/lisa.jpg"
},
new Customer
{
FirstName = "Betty",
LastName = "Bossy",
Gender = Gender.Female,
WebSite = new Uri("http://www.bettybossy.ch"),
Image = "Images/betty.jpg"
}
};
Customers = CollectionViewSource.GetDefaultView(_customers);
GroupedCustomers = new ListCollectionView(_customers);
GroupedCustomers.GroupDescriptions.Add(new PropertyGroupDescription("Gender"));
MyRotatedDataGrid.DataContext = this;
}
}
public enum Gender
{
Male,
Female
}
public class Customer : INotifyPropertyChanged
{
private string _firstName;
private string _lastName;
private Gender _gender;
private Uri _webSite;
private bool _newsletter;
private string _image;
public string FirstName
{
get { return _firstName; }
set
{
_firstName = value;
NotifyPropertyChanged("FirstName");
}
}
public string LastName
{
get { return _lastName; }
set
{
_lastName = value;
NotifyPropertyChanged("LastName");
}
}
public Gender Gender
{
get { return _gender; }
set
{
_gender = value;
NotifyPropertyChanged("Gender");
}
}
public Uri WebSite
{
get { return _webSite; }
set
{
_webSite = value;
NotifyPropertyChanged("WebSite");
}
}
public bool ReceiveNewsletter
{
get { return _newsletter; }
set
{
_newsletter = value;
NotifyPropertyChanged("Newsletter");
}
}
public string Image
{
get { return _image; }
set
{
_image = value;
NotifyPropertyChanged("Image");
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
#region Private Helpers
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
}
Ok I found how to solve it.
The transformation applied into the DataGridCell was creating this scrollviewer problem. To solve that, I removed the layout transform on the DataGridCell (by removing the BaseOn code) and I applied the transformation into the DataGridCell Template.
WRONG
<Style TargetType="DataGridCell" BasedOn="{StaticResource DataGridBase}"/>
RIGHT
<Style TargetType="DataGridCell">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridCell}">
<Grid>
<Grid.LayoutTransform>
<TransformGroup>
<RotateTransform Angle="90" />
<MatrixTransform Matrix="-1, 0, 0, 1, 0, 0" />
</TransformGroup>
</Grid.LayoutTransform>
<ContentPresenter/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I've a WrapPanel surrounded with a ScrollViewer, I want to find the visible elements on the screen when I click a button.
My code like:
<ScrollViewer>
<WrapPanel>
<Label Width="500" Height="500"/>
<Label Width="500" Height="500"/>
<Label Width="500" Height="500"/>
....
....
....
<Label Width="500" Height="500"/>
<Label Width="500" Height="500"/>
<Label Width="500" Height="500"/>
</WrapPanel>
</ScrollPanel>
How can I find the visible Label elements when ScrollViewer scrolled to some offset.
Hopefully this will get you started (you may want to change 'svViewportBounds.IntersectsWith' with 'Contains'.
public partial class MainWindow : Window
{
public string VisibleItems
{
get { return (string)GetValue(VisibleItemsProperty); }
set { SetValue(VisibleItemsProperty, value); }
}
public static readonly DependencyProperty VisibleItemsProperty =
DependencyProperty.Register("VisibleItems", typeof(string), typeof(MainWindow), new PropertyMetadata("??"));
public List<string> Items { get; private set; }
public MainWindow()
{
Items = new List<string>();
for (int i = 0; i < 25; ++i )
{
Items.Add("item_" + i);
}
DataContext = this;
InitializeComponent();
}
void OnScrollViewerScrollChanged(object sender, ScrollChangedEventArgs e)
{
ScrollViewer sv = (ScrollViewer)sender;
var visibleItems = new List<int>();
Rect svViewportBounds = new Rect(sv.HorizontalOffset, sv.VerticalOffset, sv.ViewportWidth, sv.ViewportHeight);
for(int i = 0; i < Items.Count; ++i)
{
var container = itemsHost.ItemContainerGenerator.ContainerFromIndex(i) as FrameworkElement;
if(container != null)
{
var offset = VisualTreeHelper.GetOffset(container);
var bounds = new Rect(offset.X, offset.Y, container.ActualWidth, container.ActualHeight);
if (svViewportBounds.IntersectsWith(bounds))
{
visibleItems.Add(i);
}
}
}
VisibleItems = string.Join(", ", visibleItems.ToArray());
}
}
<Window x:Class="WpfApplication59.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
WindowStartupLocation="CenterScreen"
Width="400"
Height="400">
<DockPanel>
<Border BorderThickness="1"
BorderBrush="Black"
DockPanel.Dock="Top">
<TextBlock Text="{Binding VisibleItems}"
Margin="5" />
</Border>
<ItemsControl ItemsSource="{Binding Items}" Name="itemsHost">
<ItemsControl.Template>
<ControlTemplate>
<ScrollViewer HorizontalScrollBarVisibility="Disabled"
ScrollChanged="OnScrollViewerScrollChanged">
<WrapPanel IsItemsHost="True" />
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderThickness="1"
BorderBrush="Black"
Width="100"
Height="100">
<Label Content="{Binding}" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
<Style TargetType="{x:Type ContentPresenter}">
<Setter Property="Margin"
Value="4" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</DockPanel>
I copied an example about dock panel from "WPF 4.5 Unleashed" ch.5. In the example the dock is set to be in the right of the grid So when in the code behind layer0.ColumnDefinitions.Add(column1CloneForLayer0); called, a column with content column1CloneForLayer0 will be created in the right side of the grid.
In my project I almost did the same thing, but when I call the function to add a column with some content, some empty space's created at the right side.. which is not as expected.So what's the right thing to do?
1.How to Add a column in the left side of a grid?
2.In the example the column was created yet why it's empty? Maybe Do i need to set the Z Index?
Update
I added something similar in the original example in the book and get wrong result. Below is the the source codes. I just added a leftToolBox in the left.
https://www.dropbox.com/sh/61hm139j77kz9k1/AACKqhG5uXFkQgnt8fWi4NvNa?dl=0
Update2
Relevant codes: In this case I just add a stackpanel with button in the left side and and click to call DocakPane(3), instead of giving the new column in the left, it creates column in the right.
public void DockPane(int paneNumber)
{
if (paneNumber == 1)
{
pane1Button.Visibility = Visibility.Collapsed;
pane1PinImage.Source = new BitmapImage(new Uri("pin.gif", UriKind.Relative));
// Add the cloned column to layer 0:
layer0.ColumnDefinitions.Add(column1CloneForLayer0);
// Add the cloned column to layer 1, but only if pane 2 is docked:
if (pane2Button.Visibility == Visibility.Collapsed) layer1.ColumnDefinitions.Add(column2CloneForLayer1);
}
else if (paneNumber == 2)
{
pane2Button.Visibility = Visibility.Collapsed;
pane2PinImage.Source = new BitmapImage(new Uri("pin.gif", UriKind.Relative));
// Add the cloned column to layer 0:
layer0.ColumnDefinitions.Add(column2CloneForLayer0);
// Add the cloned column to layer 1, but only if pane 1 is docked:
if (pane1Button.Visibility == Visibility.Collapsed) layer1.ColumnDefinitions.Add(column2CloneForLayer1);
}
else
{
leftpane1Button.Visibility = Visibility.Collapsed;
pane3PinImage.Source = new BitmapImage(new Uri("pin.gif", UriKind.Relative));
layer0.ColumnDefinitions.Add(testcol);
}
}
Based on the sample project provided, here is a re-write of the same using MVVM and most of the problem related to hard-coding would are gone. It is not pure MVVM but it is mostly MVVM to get started with.
start by defining ViewModels
a class to represent the dock pane
class DockablePaneVM : ViewModelBase
{
public DockablePaneVM(DockHostVM host)
{
Host = host;
}
private string _title;
public string Title
{
get { return _title; }
set
{
_title = value;
RaisePropertyChanged("Title");
}
}
private bool _isPinned;
public bool IsPinned
{
get { return _isPinned; }
set
{
_isPinned = value;
Host.PinModeChanged(this);
RaisePropertyChanged("IsPinned");
}
}
private object _content;
public object Content
{
get { return _content; }
set
{
_content = value;
RaisePropertyChanged("Content");
}
}
public DockHostVM Host { get; private set; }
public Dock Dock { get; set; }
}
host for the dockpanes, I have used collectionview for simplifying the location
class DockHostVM : ViewModelBase
{
public DockHostVM()
{
Panes = new ObservableCollection<DockablePaneVM>();
LeftPanes = new CollectionViewSource() { Source = Panes }.View;
RightPanes = new CollectionViewSource() { Source = Panes }.View;
FlotingLeftPanes = new CollectionViewSource() { Source = Panes }.View;
FlotingRightPanes = new CollectionViewSource() { Source = Panes }.View;
LeftPanes.Filter = o => Filter(o, Dock.Left, true);
RightPanes.Filter = o => Filter(o, Dock.Right, true);
FlotingLeftPanes.Filter = o => Filter(o, Dock.Left, false);
FlotingRightPanes.Filter = o => Filter(o, Dock.Right, false);
}
private bool Filter(object obj, Dock dock, bool isPinned)
{
DockablePaneVM vm = obj as DockablePaneVM;
return vm.Dock == dock && vm.IsPinned == isPinned;
}
public ObservableCollection<DockablePaneVM> Panes { get; set; }
public ICollectionView LeftPanes { get; set; }
public ICollectionView RightPanes { get; set; }
public ICollectionView FlotingLeftPanes { get; set; }
public ICollectionView FlotingRightPanes { get; set; }
private object _content;
public object Content
{
get { return _content; }
set { _content = value; }
}
public void PinModeChanged(DockablePaneVM paneVM)
{
LeftPanes.Refresh();
RightPanes.Refresh();
FlotingLeftPanes.Refresh();
FlotingRightPanes.Refresh();
}
//sample generator
public static DockHostVM GetSample()
{
DockHostVM vm = new DockHostVM();
vm.Panes.Add(new DockablePaneVM(vm) { Title = "Left Toolbox", Content = new ToolBoxVM() });
vm.Panes.Add(new DockablePaneVM(vm) { Title = "Solution Explorer", Content = new SolutionExplorerVM(), Dock = Dock.Right });
vm.Panes.Add(new DockablePaneVM(vm) { Title = "Toolbox", Content = new ToolBoxVM(), Dock = Dock.Right });
return vm;
}
}
then styles, to give provide the view for the classes
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:l="clr-namespace:VisualStudioLikePanes">
<DataTemplate DataType="{x:Type l:DockablePaneVM}">
<Grid Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="auto" />
</Grid.ColumnDefinitions>
<TextBlock Padding="8,4"
Text="{Binding Title}"
TextTrimming="CharacterEllipsis"
Background="{DynamicResource {x:Static SystemColors.ActiveCaptionBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.ActiveCaptionTextBrushKey}}" />
<ToggleButton IsChecked="{Binding IsPinned}"
Grid.Column="1">
<Image Name="pinImage"
Source="pinHorizontal.gif" />
</ToggleButton>
<ContentControl Content="{Binding Content}"
Grid.Row="1"
Grid.ColumnSpan="2" />
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IsPinned}"
Value="True">
<Setter Property="Source"
TargetName="pinImage"
Value="pin.gif" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
<Style TargetType="TabItem">
<Setter Property="Header"
Value="{Binding Title}" />
</Style>
<Style TargetType="TabItem"
x:Key="FloterItem"
BasedOn="{StaticResource {x:Type TabItem}}">
<Setter Property="LayoutTransform">
<Setter.Value>
<RotateTransform Angle="90" />
</Setter.Value>
</Setter>
</Style>
<Style TargetType="TabControl">
<Setter Property="l:TabControlHelper.IsLastItemSelected"
Value="True" />
<Style.Triggers>
<Trigger Property="HasItems"
Value="false">
<Setter Property="Visibility"
Value="Collapsed" />
</Trigger>
</Style.Triggers>
</Style>
<Style TargetType="TabControl"
x:Key="AutoResizePane"
BasedOn="{StaticResource {x:Type TabControl}}">
<Style.Triggers>
<Trigger Property="IsMouseOver"
Value="false">
<Setter Property="Width"
Value="23" />
</Trigger>
</Style.Triggers>
</Style>
<DataTemplate DataType="{x:Type l:ToolBoxVM}">
<ListBox Padding="10"
Grid.Row="1">
<ListBoxItem>Button</ListBoxItem>
<ListBoxItem>CheckBox</ListBoxItem>
<ListBoxItem>ComboBox</ListBoxItem>
<ListBoxItem>Label</ListBoxItem>
<ListBoxItem>ListBox</ListBoxItem>
</ListBox>
</DataTemplate>
<DataTemplate DataType="{x:Type l:SolutionExplorerVM}">
<TreeView Grid.Row="2">
<TreeViewItem Header="My Solution" IsExpanded="True">
<TreeViewItem Header="Project #1" />
<TreeViewItem Header="Project #2" />
<TreeViewItem Header="Project #3" />
</TreeViewItem>
</TreeView>
</DataTemplate>
</ResourceDictionary>
I have also created some dummy classes to represent the toolbox and solution explorer
also a helper class to improve the usability of tab control which i have used to host the dockpanes
class TabControlHelper
{
public static bool GetIsLastItemSelected(DependencyObject obj)
{
return (bool)obj.GetValue(IsLastItemSelectedProperty);
}
public static void SetIsLastItemSelected(DependencyObject obj, bool value)
{
obj.SetValue(IsLastItemSelectedProperty, value);
}
// Using a DependencyProperty as the backing store for IsLastItemSelected. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsLastItemSelectedProperty =
DependencyProperty.RegisterAttached("IsLastItemSelected", typeof(bool), typeof(TabControlHelper), new PropertyMetadata(false, OnIsLastItemSelected));
private static void OnIsLastItemSelected(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
TabControl tc = d as TabControl;
tc.Items.CurrentChanged += (ss, ee) =>
{
if (tc.SelectedIndex < 0 && tc.Items.Count > 0)
tc.SelectedIndex = 0;
};
}
}
this will keep an item selected any time, in this project it will be used when a dock pane is pinned/unpinned
now the main window, note that I have bounded the dock Panes to 4 tab controls, left, right, left float & right float
<Window x:Class="VisualStudioLikePanes.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
Height="500"
Width="800"
WindowStartupLocation="CenterScreen"
xmlns:l="clr-namespace:VisualStudioLikePanes">
<Window.DataContext>
<ObjectDataProvider MethodName="GetSample"
ObjectType="{x:Type l:DockHostVM}" />
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition />
</Grid.RowDefinitions>
<Menu>
<MenuItem Header="FILE" />
...
<MenuItem Header="HELP" />
</Menu>
<DockPanel LastChildFill="True"
Grid.Row="1">
<Border Width="23"
DockPanel.Dock="Left"
Visibility="{Binding Visibility, ElementName=LeftFloter}" />
<Border Width="23"
DockPanel.Dock="Right"
Visibility="{Binding Visibility, ElementName=RightFloter}" />
<TabControl ItemsSource="{Binding LeftPanes}"
DockPanel.Dock="Left" />
<TabControl ItemsSource="{Binding RightPanes}"
DockPanel.Dock="Right" />
<Grid Name="layer0">
... page content
</Grid>
</DockPanel>
<TabControl ItemsSource="{Binding FlotingLeftPanes}"
Grid.Row="1"
HorizontalAlignment="Left"
TabStripPlacement="Left"
Style="{StaticResource AutoResizePane}"
ItemContainerStyle="{StaticResource FloterItem}"
x:Name="LeftFloter" />
<TabControl ItemsSource="{Binding FlotingRightPanes}"
Grid.Row="1"
HorizontalAlignment="Right"
TabStripPlacement="Right"
Style="{StaticResource AutoResizePane}"
ItemContainerStyle="{StaticResource FloterItem}"
x:Name="RightFloter" />
</Grid>
</Window>
result is your expected behavior with MVVM approach, adding new panel is easy as Panes.Add(new DockablePaneVM(vm) { Title = "Left Toolbox", Content = new ToolBoxVM() }); rest is handled.
Demo
download the working sample VisualStudioLikePanes.zip
I am trying to develop a application where-in I a want to generate a random number after every three seconds, insert that number into a listBox and using DataTemplate display the ListBox as a rectangle.
This is for reference.
Now the problem is that I have used a DispatcherTimer which 'ticks' after 3 seconds but the rectangle is not updated.
I am posting my XAML and .cs code. Any hints ?
namespace ListBarGraph
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
DispatcherTimer dt = new DispatcherTimer();
DataFactory df = new DataFactory();
public MainWindow()
{
InitializeComponent();
dt.Tick += new EventHandler(dt_Tick);
dt.Interval = new TimeSpan(0, 0, 3);
dt.Start();
this.PreviewKeyDown += new KeyEventHandler(MainWindow_PreviewKeyDown);
}
void dt_Tick(object sender, EventArgs e)
{
df.GetData();
}
}
public class DataFactory
{
int number = 0;
public IEnumerable<int> GetData()
{
Random random = new Random();
number = random.Next(0, 100);
return new int[] { 0, number };
}
}
}
<Window x:Class="ListBarGraph.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ListBarGraph"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<ObjectDataProvider x:Key="someData" ObjectType="{x:Type local:DataFactory}" MethodName="GetData" />
<DataTemplate x:Key="BarChartItemsTemplate">
<Border Width="300" Height="50">
<Grid>
<Rectangle Fill="Red" StrokeThickness="2" Height="40" Width="{Binding}" HorizontalAlignment="Right" VerticalAlignment="Bottom">
<Rectangle.LayoutTransform>
<ScaleTransform ScaleX="1.5"/>
</Rectangle.LayoutTransform>
</Rectangle>
</Grid>
</Border>
</DataTemplate>
<ItemsPanelTemplate x:Key="BarChartItemsPanel">
<VirtualizingStackPanel IsItemsHost="True">
<VirtualizingStackPanel.LayoutTransform>
<TransformGroup>
<RotateTransform Angle="90"/>
<ScaleTransform ScaleX="-1" ScaleY="1"/>
</TransformGroup>
</VirtualizingStackPanel.LayoutTransform>
</VirtualizingStackPanel>
</ItemsPanelTemplate>
</Window.Resources>
<Grid>
<ListBox ItemsSource="{Binding Source={StaticResource someData}}" ItemTemplate="{DynamicResource BarChartItemsTemplate}" ItemsPanel="{DynamicResource BarChartItemsPanel}"/>
</Grid>
</Window>
Your XAML is bound to one instance of DataFactory as created by the ObjectProvider, whilst your code-behind creates another instance altogether, to which the UI is not bound.
Try this to get you started. In your XAML, remove the ObjectProvider and change your ListBox to:
<ListBox ItemsSource="{Binding}" ...
Inside dt_Tick, do this:
this.DataContext = df.GetData();