WPF/AvalonDock v2.0 LayoutDocument - wpf

How can I add programmatically LayoutDocument with some of UIElements inside it? (like stackpanel, scrollviewer etc.) I'd like to add new LayoutDocument with stackpanel, canvas etc. to LayoutDocumentPane when user clicks "New project" button. May I somehow clone xaml code from one LayoutDocument and load it's to new one? And is it possible to bind Title LayoutDocument property to ViewModel Property? ( i get error it has to be dependency property )

You can use Content property. For example if you want to add a new LayoutDocument with a custom content (StackPanel e.g.) you could do it as follow:
//Get the main LayoutDocumentPane of your DockingManager
var documentPane = dockManager.Layout.Descendents().OfType<LayoutDocumentPane>().FirstOrDefault();
if (documentPane != null)
{
LayoutDocument layoutDocument = new LayoutDocument {Title = "New Document"};
//*********Here you could add whatever you want***********
layoutDocument.Content = new StackPanel();
//Add the new LayoutDocument to the existing array
documentPane.Children.Add(layoutDocument);
}

First, in XAML - give the name to the Grid, for example, x:Name = "mainGrid"
Then in class write this
//Create button - we put this in document
Button mybutton = new Button();
mybutton.Content = "hello";
mybutton.Width = 100;
mybutton.Height = 50;
mybutton.Click += (sender, ev) => { MessageBox.Show("Hello"); };
DockingManager dockmanager = new DockingManager();
//Set theme
dockmanager.Theme = new Xceed.Wpf.AvalonDock.Themes.ExpressionLightTheme();
//Create LayoutRoot
var layoutroot = new Xceed.Wpf.AvalonDock.Layout.LayoutRoot();
//Create LayoutPanel
var layoutpanel = new Xceed.Wpf.AvalonDock.Layout.LayoutPanel();
//Create LayoutDocumentPane
var layoutdocpane = new Xceed.Wpf.AvalonDock.Layout.LayoutDocumentPane();
//Create LayoutDocument and set parameters of Document
var LayoutDocument = new Xceed.Wpf.AvalonDock.Layout.LayoutDocument();
LayoutDocument.Title = "Some text";
//Put button in Document
LayoutDocument.Content = mybutton;
layoutdocpane.Children.Add(LayoutDocument);
layoutpanel.Children.Add(layoutdocpane);
layoutroot.RootPanel.Children.Add(layoutpanel);
dockmanager.Layout = layoutroot;
mainGrid.Children.Add(dockmanager);
Sorry for my poor English. Please rewrite this, if it would be helpful.

I'm not that familiar with WPF and especially AvalonDock. I did it like this and it works so far :)
You can write a separate class for your documents that inherits from LayoutDocument. In that way you should be able to edit the standard layout of your "Project-Document" with the VisualStudio Designer (add your stackpanel, canvas etc.).
(I assume that you have a standard way of displaying your "Project-Document". Otherwise you could build the content yourself in code behind like you would do in WPF and put it inside the LayoutDocument.)
For example:
<ad:LayoutDocument x:Class="Namespace.MyDocument"
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"
xmlns:ad="http://avalondock.codeplex.com"
d:DesignHeight="500"
d:DesignWidth="800"
<Grid>
<!-- content -->
</Grid>
</ad:LayoutDocument>
And the class in code behind that inherits from LayoutDocument:
namespace Namespace
{
public partial class MyDocument : AvalonDock.Layout.LayoutDocument
{
// ...
}
}
To create and add a new document you just instantiate a new MyDocument object and add it to the collection via binding or something like layoutDocumentPane.Children.Add(doc).
I don't know about the binding for the Title Property, though.

Thats exactly right.
You can add the title by just adding doc.Title = "My document title"
or
You can add Title="My document title" in the document.xaml which is going to be the child.

Related

How to rebuild logical tree in WPF without showing window

I have a test code that add a TabItem to TabControl.
But when I try to find the TabItem by name, null is return.
I found a solution is show the window, then I can find the TabItem by name.
But when many tests are running, OutOfMemory exception is occurred because many windows are opened.
Is there another solution to rebuild logical tree without showing window?
The following is my test code
[TestMethod]
public void MyTest2()
{
// Arrange
// Initilize a subVM of CMSEditorViewModel type
var subVM = new SubViewModel();
// Initialize a mainVM of CMSEditorMainViewModel type
var mainVM = new MainViewModel();
// Initialize a MainWindow of DynamicCMS.Exe.CMSEditor
var mainWindow = new MyEditor.MainWindow();
mainWindow.DataContext = mainVM;
ContentPresenter presenter = new ContentPresenter();
using (var stream = System.IO.File.OpenRead(CmsPath.DirViewWithBS + "Subscreen.xaml"))
{
DataTemplate template = XamlReader.Load(stream) as DataTemplate;
presenter.ContentTemplate = template;
presenter.Content = subVM;
}
// Create a TabItem of TabControl
TabItem item = new TabItem();
item.Header = "Tab1";
item.Content = presenter;
item.Name = "tab1";
// Get "mainTabControl" TabControl from MainWindow
CustomTabControl tab = CmsUtil.GetControl((Visual)mainWindow.Content, "mainTabControl") as CustomTabControl;
// Add TabItem to TabControl
tab.Items.Add(item);
mainWindow.Show() // After showing window, I can find the TabItemControl
// Act
TabItem tabItem = (TabItem)CmsUtil.GetControl((Visual)mainWindow.Content, "tab1");
// Assert
Assert.IsNotNull(tabItem);
}

MainWindow child in wpf application

public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
System.Windows.Controls.Button b = new System.Windows.Controls.Button();
System.Windows.Shapes.Rectangle r = new System.Windows.Shapes.Rectangle();
r.Width = 40;
r.Height = 40;
r.Fill = System.Windows.Media.Brushes.Black;
b.Content = r; // Make the square the content of the Button
this.AddChild(b);
}
}
I have code for button from some WPF 4 book, and i want to display from here ( not from XAML), but when i want to add button 'b' as a child of main window i get exception and info : Content of a ContentControl must be a single element.
How can i display it in c#?
As you say this line
this.AddChild(b);
wont work as the error points out it requires a single element (ie Grid, StackPanel)
Give your Grid in xaml a name
MainWindow.xaml
<Grid x:Name="root">
</Grid>
and add your button to the Grid in MainWindow.xaml.cs
//this.AddChild(b); //wont work cant add button to this(MainWindow)
root.Children.Add(b); //adds button to the Grid of MainWindow

WPF ListBox ItemsSource bindings does not find Path referenced

I'm new to WPF and I want to do an app which shows pictures in a ListBox.
I made a class called _Images, that gets images from a local folder, puts on a list.
namespace wpfAppSlides.Imagens
{
class _Images
{
public List<Image> imgList = new List<Image>();
public void CarregaImagens()
{
string dir = #"C:\Users\devUser\Img";
foreach (string file in Directory.GetFiles(dir))
{
Image image = new Image();
BitmapImage source = new BitmapImage();
source.BeginInit();
source.UriSource = new Uri(file, UriKind.Relative);
source.EndInit();
image.Source = source;
imgList.Add(image);
}
}
}
}
An then, in my XAML, I referenced my namespaces.
xmlns:is="clr-namespace:wpfAppSlides._Images"
xmlns:col="clr-namespace:wpfAppSlides"
And put in a DataContext.
<Window.DataContext>
<is:_Images></is:_Images>
</Window.DataContext>
But when I wanna feed my ListBox with the items on imgList, using ItemsSource (in Properties, not code), he does not find any Path that I can relate to imgList.
ListBox XAML:
<ListBox Height="110"
HorizontalAlignment="Left"
Margin="14,50,0,0"
Name="listBox1"
VerticalAlignment="Top"
Width="477" ItemsSource="{Binding}" />
MainWindow code:
public partial class MainWindow : Window
{
_Imagens imgs;
public MainWindow()
{
InitializeComponent();
imgs = (_Images)this.DataContext;
imgs.CarregaImagens();
}
}
I tried to use ObservableCollection, but it's no use.
Help? ):
I noticed a couple of things..
looks like your class _Images isn't declared as public
When constructor of that class is called (happens when you added it as Window.DataContext,
how/where does CarregaImagens() get called. Does it ever get called? If not, put in in the default constructor.
You shouldn't name a class with the underscore (that's the convention for a private member)
you know that in order to bind in xaml, the property cannot be private?
hoper these help...

How can you set the DataContext dynamically in C#-code?

I builded a RibbonGroupBox like this in a C# file:
public class TextControl : RibbonGroupBox
{
public TextControl()
{
const double widthOfComboBoxes = 150;
Binding fontsBinding = new Binding();
fontsBinding.Source = (TextControlVM)DataContext;
fontsBinding.Path = new System.Windows.PropertyPath("Fonts");
fontsBinding.Mode = BindingMode.TwoWay;
Binding fontSizeBinding = new Binding();
fontSizeBinding.Source = (TextControlVM)DataContext;
fontSizeBinding.Path = new System.Windows.PropertyPath("FontSize");
fontSizeBinding.Mode = BindingMode.TwoWay;
/* Combobox for the fonts (Arial, etc.) */
Fluent.ComboBox fontCombo = new Fluent.ComboBox();
fontCombo.SetBinding(Fluent.ComboBox.ItemsSourceProperty, fontsBinding);
fontCombo.SelectedItem = ((TextControlVM)DataContext).DefaultFont;
fontCombo.Width = widthOfComboBoxes;
this.AddChild(fontCombo);
/* Combobox for the fontsizes */
Fluent.ComboBox fontSizeCombo = new Fluent.ComboBox();
fontSizeCombo.SetBinding(Fluent.ComboBox.ItemsSourceProperty, fontSizeBinding);
fontSizeCombo.SelectedItem = ((TextControlVM)DataContext).DefaultFontSize;
fontSizeCombo.Width = widthOfComboBoxes;
this.AddChild(fontSizeCombo);
}
}
I furthermore have a viewmodel (TextControlVM) that contains Properties for Fonts, FontSize, DefaultFont and DefaultFontSize.
When I now use this in another module like this, the DataContext in the above example is null:
<Fluent:RibbonTabItem Header="Export">
<TextControl DataContext="{Binding DataContext.TextControl}"/>
</Fluent:RibbonTabItem>
When I build the RibbonGroupBox with XAML code everything works fine, so I want to do what XAML automatically does. How can I do that?
Background: I want to use the RibbonGroupBox in several modules. That is why I build it with C#-Code, so that I can access it dynamically. The DataContext will change dependend on the call.
The DataContext is implied in a binding automatically, so you are essentially binding to RibbonTabItem.DataContext.DataContext.TextControl, which doesn't exist
To bind to RibbonTabItem.DataContext.TextControl, simply leave the extra DataContext out of the binding
<Fluent:RibbonTabItem Header="Export">
<TextControl DataContext="{Binding TextControl}"/>
</Fluent:RibbonTabItem>

How to update the texblock string globally in Silverlight?

I need to update textblock inside childWindow based on file name that is being selected with OpenDialog Window. Since I am not running OpenDialog from childWindow I have trouble passing that value to the texblock inside ChildWindow. I am wondering if someone can help. As a result of th issue I have, I am wondering if it is possible to have OpenDialog inside ChildWindow? Thank you for any ideas!
ChildWindow xaml:
<sdk:ChildWindow
x:Class="AddPackages_ChildWindow"
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:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
xmlns:toolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit"
AutomationProperties.AutomationId="AddPackages_ChildWindow">
<Grid x:Name="AddPackages_ChildWindow_LayoutRoot" AutomationProperties.AutomationId="AddPackages_ChildWindow_LayoutRoot" Style="{StaticResource AVV_GridStyle}">
<TextBlock x:Name="txtUpdate_Package" AutomationProperties.AutomationId="txtUpdate_Package" Text="FileName" /> </Grid>
Below is the code to open DialogBox and passing selected file name:
private void Package_Click(object sender, System.Windows.RoutedEventArgs e)
{
AddPackage_ChildWindow ap = new AddPackage_ChildWindow();
ap.Show();
OpenFileDialog openFileDialog1 = new OpenFileDialog();
openFileDialog1.Filter = "App-V Packages (*.sprj)|*.sprj|App-V Packages (*.sprj)|*.sprj";
openFileDialog1.FilterIndex = 1;
openFileDialog1.Multiselect = true;
bool? userClickedOK = openFileDialog1.ShowDialog();
if (userClickedOK == true)
{
//passing the file name string
txtUpdate_Package.Text = openFileDialog1.File.Name;
System.IO.Stream fileStream = openFileDialog1.File.OpenRead();
using (System.IO.StreamReader reader = new System.IO.StreamReader(fileStream))
{
// Read the first line from the file and write it the textbox.
// txtUpdate_Package.Text = reader.ReadLine();
}
fileStream.Close();
}
}
You could expose a SetText method on your ChildWindow class like so:
public void SetText(string text) {
this.txtUpdate_Package.Text = text;
}
Then you'd call it like so from your Package_Click method:
ap.SetText(reader.ReadLine());
If you're not too concerned with what the the OO purists think you can change this line in your code:-
txtUpdate_Package.Text = openFileDialog1.File.Name;
to this:-
ap.txtUpdate_Package.Text = openFileDialog1.File.Name;
This works because the auto-generated class file created for your child window Xaml will have a field of type TextBlock called txtUpdate_Package with the access of internal, i.e.
internal TextBlock txUpdate_Package;
This field is is assigned during the ChildWindow's InitializeComponent method called as part of its constructor.
However, I would prefer to create a public property to be used to handle this rather than write code the relies on what should be considered the private internal structure. Add this property to the Code behind of your child window.
public string Text
{
get { return txtUpdate_Package.Text; }
set { txtUpdate_Package.Text = value; }
}

Resources