How can I put an user control inside a document viewer? - c#

Is it possible put an user control inside a doument viewer? If possible, how will it be that?

You can use the following..
Edit
Added a Grid which binds its Width/Height to the FixedPage ActualWidth/ActualHeight to achieve centering
<DocumentViewer>
<FixedDocument>
<PageContent>
<FixedPage HorizontalAlignment="Center">
<Grid Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type FixedPage}},
Path=ActualWidth}"
Height="{Binding RelativeSource={RelativeSource AncestorType={x:Type FixedPage}},
Path=ActualHeight}">
<local:MyUserControl HorizontalAlignment="Center"/>
</Grid>
</FixedPage>
</PageContent>
</FixedDocument>
</DocumentViewer>
Unfortunately the Visual Studio 2010 designer is broken here and you'll get a message saying "Property 'pages' does not support values of type 'PageContent`.
This is reported here: WPF FixedDocument object doesn't allow PageContent children
As a workaround you can load it in code behind
Xaml
<DocumentViewer>
<FixedDocument Loaded="FixedDocument_Loaded"/>
</DocumentViewer>
Code behind
private void FixedDocument_Loaded(object sender, RoutedEventArgs e)
{
FixedDocument fixedDocument = sender as FixedDocument;
MyUserControl myUserControl = new MyUserControl();
myUserControl.HorizontalAlignment = HorizontalAlignment.Center;
myUserControl.VerticalAlignment = VerticalAlignment.Center;
Grid grid = new Grid();
grid.Children.Add(myUserControl);
FixedPage fixedPage = new FixedPage();
fixedPage.Children.Add(grid);
Binding widthBinding = new Binding("ActualWidth");
widthBinding.Source = fixedPage;
Binding heightBinding = new Binding("ActualHeight");
heightBinding.Source = fixedPage;
grid.SetBinding(Grid.WidthProperty, widthBinding);
grid.SetBinding(Grid.HeightProperty, heightBinding);
PageContent pageContent = new PageContent();
(pageContent as IAddChild).AddChild(fixedPage);
fixedDocument.Pages.Add(pageContent);
}

Related

bubble scroll event from FlowDocumentScrollViewer to ListView

I have the following (part of) XAML:
<ListView x:Name="logView" Grid.Row="2" ItemsSource="{Binding Logs}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListView.ItemTemplate>
<DataTemplate>
<FlowDocumentScrollViewer ScrollViewer.VerticalScrollBarVisibility="Disabled"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<FlowDocument FontSize="12" FontFamily="Calibri" PagePadding="0" TextAlignment="Left">
<Paragraph TextIndent="-10" Margin="10,0,0,0">
<Run Text="{Binding .}" />
</Paragraph>
</FlowDocument>
</FlowDocumentScrollViewer>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Logs is an IEnumerable<string> that the ListView is bound to (via a ViewModel, but that shouldn't matter here).
If I remove the whole <ListView.ItemTemplate>...</ListView.ItemTemplate>, I have the mouse wheel scrolling behaviour I want. But with the FlowDocumentScrollViewer and its content, scrolling does not work as smoothly any more. It still scrolls, but just every now and then, most of the time it gets stuck.
Trying to solve this, I followed this solution and created a PreviewMouseWheel handler in the codebehind
private void BubbleScrollingToLogView(object sender, MouseWheelEventArgs e)
{
if (!e.Handled)
{
e.Handled = true;
var eventArg = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta);
eventArg.RoutedEvent = MouseWheelEvent;
eventArg.Source = sender;
logView.RaiseEvent(eventArg);
}
}
and added it in the XAML:
....
<FlowDocumentScrollViewer ScrollViewer.VerticalScrollBarVisibility="Disabled"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
PreviewMouseWheel="BubbleScrollingToLogView">
....
But it did not make any change in behaviour. I even tried adding PreviewMouseWheel="BubbleScrollingToLogView" to <FlowDocument> and <Paragraph>, assuming that those might catch the event as well. But nothing helped.
So what do I need to do to get the smooth, default scrolling behaviour of the ListView?
If you decide to use only FlowDocumentScrollViewer you can bind Document property to your property on view model.
<FlowDocumentScrollViewer Document="{Binding Document}" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
</FlowDocumentScrollViewer>
In your view model define Document property of FlowDocument type.
private FlowDocument document;
public FlowDocument Document
{
get { return document; }
set
{
document = value;
OnPropertyChanged();
}
}
Create FlowDocument from Logs:
var doc = new FlowDocument();
doc.FontSize = 12;
doc.FontFamily = new FontFamily("Calibri");
doc.PagePadding = new Thickness(0);
doc.TextAlignment = TextAlignment.Left;
foreach (var log in Logs)
{
var paragraph = new Paragraph(new Run(log));
paragraph.TextIndent = -10;
paragraph.Margin = new Thickness(10, 0, 0, 0);
doc.Blocks.Add(paragraph);
}
Document = doc;
It was enough to disable the FlowDocumentScrollViewer to get the default scrolling behaviour. I didn't notice any difference in the appearance due to disabling.
<FlowDocumentScrollViewer ScrollViewer.VerticalScrollBarVisibility="Disabled"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
IsEnabled="False">

WPF add TabItem with behavior programmatically

In xaml when I want to add some behavior I do like this:
<!-- XAML -->
<TabItem behaviors:TabItemValidationBehavior.ActivateValidation ="True">
<TabItem.Header>
<TextBlock Text="Header"
Foreground="{Binding Path=(behavior:TabItemBehavior.Foreground), RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type TabItem}}}" />
</TabItem.Header>
</TabItem>
It is possible to do the same programmatically?
// C#
TabItem tab = new TabItem();
??tab.AddBehavior(behaviors:TabItemValidationBehavior.ActivateValidation(True));??
??tab.Header= new TextBlock { Foreground.BindTo(behavior:TabItemBehavior.Foreground, tab) };??
How to achieve that?
Behavior exposes an AttachedProperty. You can set it like
TabItem tab = new TabItem();
TabItemValidationBehavior.SetActivateValidation(tab, true);
TextBlock text = new TextBlock();
Binding binding = new Binding();
binding.Path = new PropertyPath(TabItemBehavior.ForegroundProperty);
binding.RelativeSource = new RelativeSource{Mode = RelativeSourceMode.FindAncestor, AncestorType = typeof(TabItem)};
text.SetBinding(TextBlock.ForegroundProperty, binding);
tab.Header=text;

Textboxes binded to SelectedItem of a code-behind created DataGrid don't actualize

I'm experimenting with code-behind created WPF masks as prototype for a WPF mask designer.
In my ViewModel i have a DataTable and a DataView (which is simply the DefaultView of the DataTable).
In my DataTable i got two columns ("vorname" and "nachname") and four rows.
In my WPF mask i want to have a DataGrid and two TextBoxes, which are binded to the SelectedItem of the DataGrid and the columns (either "vorname" or "nachname").
When i select an item in the DatGrid at runtime, the data from that item shall be showed in the TextBoxes.
First i tried to define the DataGrid in the XAML file and generate the TextBoxes an their bindings in code.
Here it works fine.
I select an item in the DataGrid and the data of the item is showed in the TextBoxes.
But when i generate the grid in code, it doesn't work anymore.
Is there some sort of NotifyOnSelectedIndexChanged, that i'm missing?
Any help will be appreciated.
This is the XAML:
<Window x:Class="DesignerTest.TestWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="TestWindow"
Height="400"
Width="600">
<DockPanel x:Name="mainpanel">
<!--<DataGrid x:Name="datagrid"
DockPanel.Dock="Top"
Height="120" />-->
<WrapPanel x:Name="wrappanel">
<!--<TextBox x:Name="vornameSelected" Width="150" Margin="5" Text="{Binding SelectedItem.vorname, ElementName=datagrid}" IsEnabled="False" />
<TextBox x:Name="nachnameSelected" Width="150" Margin="5" Text="{Binding SelectedItem.nachname, ElementName=datagrid}" IsEnabled="False" />-->
<!--<TextBox x:Name="vornameSelected" Width="150" Margin="5" IsEnabled="False" />
<TextBox x:Name="nachnameSelected" Width="150" Margin="5" IsEnabled="False" />-->
</WrapPanel>
</DockPanel>
</Window>
And this is the code for creating and binding:
// The ViewModel und the DataTable are created.
_vm = new SerializingTestViewModel();
_vm.CreateDataTable();
this.DataContext = _vm.DataTable;
// The DataGrid and it's Binding are created.
DataGrid datagrid = new DataGrid();
datagrid.Name = "datagrid";
DockPanel.SetDock(datagrid, Dock.Top);
datagrid.Height = 120;
datagrid.ItemsSource = _vm.DataSource;
mainpanel.Children.Add(datagrid);
// The Textboxes and the Bindings are created.
TextBox vornameSelected = new TextBox();
vornameSelected.Name = "vornameSelected";
vornameSelected.Width = 150;
Thickness margin = new Thickness(5);
vornameSelected.SetValue(TextBox.MarginProperty, margin);
vornameSelected.IsEnabled = false;
Binding selectedItemBinding = new Binding();
selectedItemBinding.ElementName = "datagrid";
selectedItemBinding.Path = new PropertyPath("SelectedItem.vorname");
vornameSelected.SetBinding(TextBox.TextProperty, selectedItemBinding);
wrappanel.Children.Add(vornameSelected);
TextBox nachnameSelected = new TextBox();
nachnameSelected.Name = "nachnameSelected";
nachnameSelected.Width = 150;
margin = new Thickness(5);
nachnameSelected.SetValue(TextBox.MarginProperty, margin);
nachnameSelected.IsEnabled = false;
selectedItemBinding = new Binding();
selectedItemBinding.ElementName = "datagrid";
selectedItemBinding.Path = new PropertyPath("SelectedItem.nachname");
nachnameSelected.SetBinding(TextBox.TextProperty, selectedItemBinding);
wrappanel.Children.Add(nachnameSelected);
Try setting your binding source using the Source property instead of ElementName
//selectedItemBinding.ElementName = "datagrid"
selectedItemBinding.Source = datagrid;
The problem might be that the ElementName lookup for items is not working as expected because items are added dynamically at runtime via code behind.

How to convert XAML(ControlTemplate) to Code-Behind

<dxg:LookUpEdit Margin="5" HorizontalAlignment="Stretch" VerticalAlignment="Center"
ItemsSource="{Binding}"
DisplayMember="Name"
AutoPopulateColumns="False">
<dxg:LookUpEdit.PopupContentTemplate>
<ControlTemplate>
<dxg:GridControl Name="PART_GridControl">
<dxg:GridControl.Columns>
<dxg:GridColumn FieldName="Name"/>
<dxg:GridColumn FieldName="Date"/>
</dxg:GridControl.Columns>
</dxg:GridControl>
</ControlTemplate>
</dxg:LookUpEdit.PopupContentTemplate>
</dxg:LookUpEdit>
please help me, I do not know how to add GridColumn on GridControl and set LookUpEdit.PopupContentTemplate = GridControl
Thank you very much. I think XAMLREADER can help me, I'm stuck here
ControlTemplate ct = new ControlTemplate();
FrameworkElementFactory gridcontrol = new FrameworkElementFactory(typeof(GridControl));
FrameworkElementFactory gridcolumn = new FrameworkElementFactory(typeof(GridColumn));
gridcontrol.SetValue(?, ?);
gridcolumn.SetValue(?, ?);
ct.VisualTree = gridcontrol;
creating control/data templates in code behind involves factory classes, that may be more complicated for non framework types.
example from Creating a control template with code behind
ControlTemplate template = new ControlTemplate(typeof(ListBoxItem));
FrameworkElementFactory elemFactory = new FrameworkElementFactory(typeof(Border));
elemFactory.Name = "Border";
elemFactory.SetValue(Border.CornerRadiusProperty, new CornerRadius(5));
elemFactory.SetValue(Border.PaddingProperty, new Thickness(1));
elemFactory.SetValue(Border.SnapsToDevicePixelsProperty, true);
template.VisualTree = elemFactory;
//same can be used as
LookUpEdit.PopupContentTemplate = template;
above is just an example, you may need to use the appropriate types
alternatively keeping the control templates in resource dictionary may help you
or the easiest solution is to parse the xaml in code behind
const string xaml = "<ControlTemplate><dxg:GridControl Name=""PART_GridControl""><dxg:GridControl.Columns><dxg:GridColumn FieldName=""Name""/><dxg:GridColumn FieldName=""Date""/></dxg:GridControl.Columns></dxg:GridControl></ControlTemplate>";
ControlTemplate template = XamlReader.Parse(xaml) as ControlTemplate;
//this can be used as
LookUpEdit.PopupContentTemplate = template;

Auto Height of tabitem wpf

I'd like to create a tabcontrol through code with 2 tabs . I used the code below :
TabControl tb = new TabControl();
tb.HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch;
tb.VerticalAlignment = System.Windows.VerticalAlignment.Stretch;
tb.VerticalContentAlignment = System.Windows.VerticalAlignment.Stretch;
tb.HorizontalContentAlignment = System.Windows.HorizontalAlignment.Stretch;
ressource_design.initialiserTabControl(tb);
tb.Margin = new Thickness(10, 10, 10, 10);
TabItem ti1 = new TabItem();
ti1.Header = ServicesLangue.RM.GetString("CONTENU_ACCUEIL_LISTE_SAS");
ti1.Content = _listeSAS;
tb.Items.Add(ti1);
TabItem ti2 = new TabItem();
ti2.Header = ServicesLangue.RM.GetString("CONTENU_ACCUEIL_TBSM");
ti2.Content = _tbsm;
tb.Items.Add(ti2);
this.DockPrincipal.Children.Add(tb);
But the height of my tabitem is the height of the children control. This is driving me crazy !
I've tried to add the code below in my child control:
Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Grid}}, Path=ActualWidth}"
Height="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type Grid}}, Path=ActualHeight}"
But this makes le child control too big !
Note: I have the same problem with the Accordion.
Thanks
Remove any static Height and Width you set in XAML or Code behind.
HorizontalContentAlignment and VerticalContentAlignment are by default set to Stretch.
If your DockPrincipal is a Dockpanel and is filling your whole form, you could set LastChildFill="True"

Categories

Resources