WPF: Printing DataGrid in multiple pages - wpf

I have DataGrid that needs to be printed in multiple pages both horizontally and vertically. Based on exhaustive searching the closest solution i have is the one found #http://www.codeproject.com/Articles/138233/Custom-Data-Grid-Document-Paginator. However, if DataGridTemplateColumn having ComboBox as its content is printed, the resultant print output is blank combobox. Below is the screenshot of the print,
http://www.filedropper.com/datagridprint
Below is the code used to create a template column while printing,
private FrameworkElement GetTableCell(Grid grid, DataGridColumn column, object item, int columnIndex, int rowIndex)
{
FrameworkElement visualElement = null;
if (column is DataGridTemplateColumn)
{
DataGridTemplateColumn templateColumn = column as DataGridTemplateColumn;
ContentControl contentControl = new ContentControl();
contentControl.Focusable = true;
contentControl.ContentTemplate = templateColumn.CellTemplate;
contentControl.Content = item;
contentControl.SetValue(Grid.ColumnProperty, columnIndex);
contentControl.SetValue(Grid.RowProperty, rowIndex);
visualElement = contentControl;
}
The above code basically creates new content control and adds the CellTemplate associated with the grid to the newly created content, which does not work. I would like to know if there is a fix for the above code, if not, is there a working solution that would print DataGrid into multiple pages (WYSIWYG).
Thanks for your help.

Related

trouble displaying flowdocument

I'm having some problems displaying the contents of a flowdocument in a flowdocumentscrollviewer. I create a generic list that holds a class which contains an int, string and a flowdocument.
In a WPF listbox, I am trying to display the flowdocument in the scrollviewer alongside a button. I use the following function called from the WPF window constructor to populate the listbox
private void populateListBox()
{
foreach(Element el in _notesList)
{
StackPanel sp = new StackPanel();
sp.Orientation = Orientation.Horizontal;
Button b = new Button();
b.Content = el._theID;
sp.Children.Add(b);
FlowDocumentScrollViewer fdsv = new FlowDocumentScrollViewer();
fdsv.MinWidth = 400;
fdsv.Document = el._theDoc;
sp.Children.Add(fdsv);
ListBoxItem lbi = new ListBoxItem();
lbi.Content = sp;
noteList.Items.Add(lbi);
}
}
But the code does not work. There are no errors but the scrollviewers are just blank in the listbox. I also tried storing the classes in an ObservableList and binding to the Document property but that didn't work either.
Any ideas what is happening?
Nevermind. I figured it out.
Further down in the program execution I was copying the flowdocument blocks to a merged document in a foreach statement. This doesn't work even if you use Blocks.ToList(). I eventually found a way to copy the document contents to another document here.

How to force DataGrid to rebuild VisualTree for its columns

I have WPF form with DataGrid. New columns can be added to the datagrid manually by user via button. This is the code to add new column:
private void ColumnAdornerAddButton_MouseDown(object sender, MouseButtonEventArgs e)
{
DataGridTextAdornerColumn column = new DataGridTextAdornerColumn();
column.Header = "New column";
column.HeaderStyle = (Style)FindResource("columnHeader");
column.AdornerTemplate = (DataTemplate)FindResource("columnAdorner");
Binding binding = new Binding("Data");
binding.Mode = BindingMode.TwoWay;
column.Binding = binding;
grid.Columns.Insert(grid.Columns.Count - 1, column);
//Add adorner
DataGridColumnHeader header = GetColumnHeaderFromColumn(column);
AddAdorner(header, column.AdornerTemplate, column.IsReadOnly);
}
private DataGridColumnHeader GetColumnHeaderFromColumn(DataGridColumn column)
{
// dataGrid is the name of your DataGrid. In this case Name="dataGrid"
List<DataGridColumnHeader> columnHeaders = GetVisualChildCollection<DataGridColumnHeader>(grid);
foreach (DataGridColumnHeader columnHeader in columnHeaders)
{
if (columnHeader.Column == column)
{
return columnHeader;
}
}
return null;
}
The problem is that after I have added the column to the grid its header is not yet generated and it is not present in visual tree. Thus I cant get header for the new column and apply adorner to it.
I have tried to call ApplyTemplate recursively on visual tree of the grid without any luck.
Is there any way to force grid to generate DataGridColumnHeader for the new column in code?
Thank you in advance.
Hi after adding columns to datagrid call the method UpdateLayOut() of DataGrid.
datagrid.UpdateLayout();
I hope this will help.
I just want to enhance the solution,
The method
datagrid.Items.Refresh();
will helps to recreate the view (Datagrid).
thus you can see the updated value in datagrid

Accessing TextBoxes in WPF DataBound ListBox with foreach

I have a databound WPF ListBox with a custom itemtemplate -> datatemplate. Part of that template is also a ListBox.
On certain event I'd like to loop through all textboxes in the ListBox and retreive their values.
Is this possible?
You should be able to do this by using the ItemContainerGenerator and finding elements in the template:
foreach (var item in lb.Items)
{
var itemContainer = lb.ItemContainerGenerator.ContainerFromItem(item) as ListBoxItem;
// Name the TextBox in the template to find it here.
var textBox = itemContainer.ContentTemplate.FindName("?????", itemContainer) as TextBox;
var value = textBox.Text;
}
(If the TextBoxes you referred to are within the ListBox which is in the template you have to dig deeper by repeating the same method.)

DataTemplate in listview

I use a listview to display data like a data matrix (columns and rows). My problem is : my items are typed : MatrixCellVM.
I tried everything I found on the net to apply a DataTemplate on this items but nothing worked.
Here is the latest technique I'm using
foreach (var col in dataMatrix.Columns)
{
//create the data template
DataTemplate cellLayout = new DataTemplate();
cellLayout.DataType = typeof(MatrixCellVM);
//set up the stack panel
FrameworkElementFactory spFactory = new FrameworkElementFactory(typeof(Grid));
spFactory.Name = "myComboFactory";
//set up value textblock
FrameworkElementFactory cellValue = new FrameworkElementFactory(typeof(TextBlock));
cellValue.SetBinding(TextBlock.TextProperty, new Binding("Value"));
cellValue.SetValue(TextBlock.ToolTipProperty, "Value");
spFactory.AppendChild(cellValue);
//set the visual tree of the data template
cellLayout.VisualTree = spFactory;
gridView.Columns.Add(new GridViewColumn{
Header = col.Name,
DisplayMemberBinding = new Binding(string.Format("[{0}]", count)),
CellTemplate = cellLayout
});
count++;
}
One more thing, when I override ToString() in MatrixCellVM, the value is displayed (but with this technique I add a context menu or give any color to my value).
I solved the problem but have another one.
So what I did is pretty simple. DisplayMemberBinding and CellTemplate can't be put together.
So I deleted the whole c# code creating the datatemplate.
The only thing that remains is :
gridView.Columns.Add(
new GridViewColumn
{
Header = col.Name,
CellTemplate = (DataTemplate)listView.Resources["MatrixCellVMTemplate"]
});
Thus, I added in my xaml code defining the datatemplate.
My new problem is that my rows are arrays of MatrixCellVM (as it is a matrix, I don't have properties and I don't know by advance how many columns I will have).
That was the point of the DisplayMemberBinding = new Binding(string.Format("[{0}]",count)) code in my top post.
How could I reproduce this behavior in my datatemplate (what is the binding in my textblock).
If anything is not clear, let me know.
Thanks

How to correctly resize scrollbar when underlying collection of a WPF ListView changes?

How to correctly resize scrollbar when underlying collection of a WPF ListView changes?
I have a WPF ListView bound to an observeable collection with several thousand items. When a large number of these are removed the view seems to only show the last item. When I move the position in the view with the thumbbar, the thumbbar resizes to reflect the new collection size. Is it possible to force the ListView and Scroll bar to synchronise when the collection changes?
I have found a work-around if anyone else has this problem.
The following code example shows the items source of the ListView bening changed on the first line. The following lines show the workaround which is just to scroll back to the first item.
this.ListViewResults.ItemsSource = this.itemsFiltered;
object firstItem = this.ListViewResults.Items.GetItemAt(0);
if(firstItem == null)
{
return;
}
this.ListViewResults.ScrollIntoView(firstItem);
Strange behaviour!!
I would try setting the binding context (Context) of the ListView to null, and then the same list again in order to refresh the bindings.
I have a different workaround which requires subclassing ListView. It's a bit more work but the result is better than just scrolling to the first item. But you'll need to adapt the ListView template such that the ScrollViewer in the template has a name (here PART_ScrollViewer) or you use another way to get the ScrollViewer object.
public class BetterListView : ListView
{
ScrollViewer sv;
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
//Get the scrollviewer in the template (I adapted the ListView template such that the ScrollViewer has a name property)
sv = (this.Template.FindName("PART_ScrollViewer", this)) as ScrollViewer;
}
protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
{
base.OnItemsChanged(e);
//Prevent the bug where the ListView doesn't scroll correctly when a lot of items are removed
if (sv != null && e.Action == NotifyCollectionChangedAction.Remove)
{
sv.InvalidateScrollInfo();
}
}
}

Resources