I got a Listview control in a window form running from PowerShell that is being populated dynamically as an activity log and I basically want it to auto scroll so the last entry always is in focus.
I can get this to work with a ListBox by just setting the Item to selected but this doesn't seem to work for a ListView. I can set it to both Selected and Focused but only when the actual form control is targeted and even then it doesn't cause the scrollbar to move.
It would be fine if I just could get it to scroll to the bottom of the page on every update but would prefer to actually focus on a specific item since I got two groups and it would be good if an item from the top group could be focused as well.
Here's parts of the code
$listView1.View = 'Details'
$ListView1.Anchor = 'Top, Bottom, Left, Right'
$ListView1.Location = '0, 399'
$ListView1.Name = "ListView1"
$ListView1.Size = '683, 200'
$ListView1.TabIndex = 10
$listView1.FullRowSelect = $true
$listView1.MultiSelect = $False
$listColumnTime.Text = "Time"
$listColumnTime.Width = 90
$listColumnAction.Text = "Action"
$listColumnAction.Width = 100
$listColumnStatus.Text = "Status"
$listColumnStatus.Width = -2
$ListGroupUpdate.Header = "Updates"
$ListGroupUpdate.Tag = 0
$ListGroupAction.Header = "Actions"
$ListGroupAction.Tag = 1
$listView1.Columns.Add($listColumnTime)|Out-Null
$listView1.Columns.Add($listColumnAction)|Out-Null
$listView1.Columns.Add($listColumnStatus)|Out-Null
$listView1.Groups.Add($ListGroupUpdate)|Out-Null
$listView1.Groups.Add($ListGroupAction)|Out-Null
And the Add function
function Add-ListViewItem
{
Param(
[ValidateNotNull()]
[Parameter(Mandatory=$true)]
[System.Windows.Forms.ListView]$ListView,
[Parameter(Mandatory=$true)]
[string[]]$Items,
[Parameter(Mandatory=$true)]
[int]$Group,
[Parameter(Mandatory=$false)]
$timestamp = (get-date -Format "MM/dd HH:mm:ss"),
[switch]$Clear)
if($Clear)
{
$ListView1.Items.Clear();
}
$listitem = New-Object 'System.Windows.Forms.ListViewItem'
$listitem.text = $Timestamp
$listitem.Group = $listview.Groups[$group]
$listitem.SubItems.AddRange($Items)
$listitem.Selected = $true
$ListView.Items.Add($listitem)
$ListView.FocusedItem = $listitem
}
Would be very grateful for any help!
BR Erik
I would do it like this.
#Dll Import
Function ScrollbarImport
{
Add-Type #"
using System;
using System.Runtime.InteropServices;
public static class Scrollbar {
[DllImport("user32.dll")]
public static extern int GetScrollPos(IntPtr hWnd, int nBar);
[DllImport("user32.dll")]
public static extern int SetScrollPos(IntPtr hWnd, int nBar, int nPos, bool bRedraw);
[DllImport("user32.dll")]
public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
}
"#
}
Saves the location of the scrollbar.
$Script:ScrollbarX = [Scrollbar]::GetScrollPos($SQLProcessesListview.handle, 0)
$Script:ScrollbarY = [Scrollbar]::GetScrollPos($SQLProcessesListview.handle, 1)
Sets the Saved Position.
[Scrollbar]::SendMessage($SQLProcessesListview.Handle, 4116 ,(0 - [Int]$Script:ScrollbarX),(0 - [Int]$Script:ScrollbarY))
[Scrollbar]::SendMessage($SQLProcessesListview.Handle, 4116 , [Int]$Script:ScrollbarX,([Int]$Script:ScrollbarY * 17))
Greetings Jannik
Related
What i want to do is the Following
Create an UserControle like this:
public partial class MyUserControle : UserControl
with a Wrappanel inside to add Controls and stuff to
<WrapPanel Margin="0,0,0,41" Name="wrapPanel1" />
this class has 2 Buttons to Cancel or Apply the Settings in the Window.
The window later gets open like this
Window w = new Window();
w.SizeToContent = System.Windows.SizeToContent.WidthAndHeight;
w.Content = myClass.getmyUserControle();
bool? t = w.ShowDialog();
Where myClass is a special Class that creates the UserControle and adds some stuff it wants to be changed in the WrapPanel
Now in myClass say i have a simple Point
System.Drawing.Point myPoint = new Point(10,15);
and getmyUserControle() looks like
public MyUserControle getmyUserControle(){
MyUserControle UC = new MyUserControle();
WPF.TextBox X1 = new WPF.TextBox();
System.Windows.Data.Binding BindingX = new System.Windows.Data.Binding("X");
BindingX.Source = myPoint;
BindingX.Mode = System.Windows.Data.BindingMode.TwoWay;
X1.SetBinding(WPF.TextBox.TextProperty, BindingX);
UC.addhere.Add(X1);
return UC;
}
The Textbox is filled properly but the changes dont change the source. How can i Fix this?
edit:
I can add a
private MyUserControle myUCsave = null;
to the class and set the
myUCsave = UC;
at the end of getmyUserControle() and check at the start
myUCsave != null return myUCsave;
that fixes the "save" of the textbox, but the underlying myPoint doesnt change.
Edit: Maybe there is a more simple Case: i create a simple Form and add this to the MainWindow via constructor
public partial class MainWindow : Window
public MainWindow()
{
InitializeComponent();
TextBox X1 = new TextBox();
TextBox Y1 = new TextBox();
X1.Margin = new Thickness(0, 0, 20, 20);
Y1.Margin = new Thickness(0, 0, 100, 100);
X1.Width = 100;
Y1.Width = 100;
X1.Height = 200;
Y1.Height = 200;
System.Windows.Data.Binding BindingX = new System.Windows.Data.Binding("X");
System.Windows.Data.Binding BindingY = new System.Windows.Data.Binding("X");
BindingX.Mode = System.Windows.Data.BindingMode.TwoWay;
BindingY.Mode = System.Windows.Data.BindingMode.TwoWay;
BindingX.Source = myPoint;
BindingY.Source = myPoint;
X1.SetBinding(TextBox.TextProperty, BindingX);
Y1.SetBinding(TextBox.TextProperty, BindingY);
this.MainGrid.Children.Add(Y1);
this.MainGrid.Children.Add(X1);
}
Point myPoint = new Point(100, 200);
This Should Create Two TextBoxes X1,Y1 that are Linked to the same source (myPoint.X)? But when i change one thing the other textbox doesn't change neither does the source.
What I'm trying to do it a TreeView that have several columns. The first column is a ComboBox, which means that I use Gtk.CellRendererCombo. The thing is, when I selected a value from the ComboBox, I'd like the Text from the cell to change from "" to the value I just selected. It feasible if at the Gtk.CellRendererCombo.Edited event I set the columns Text field to the EditedArgs.NewText.
The problem is, each time I set a value, I create a new row, and I'd like the Text Field to act like it does in the Gtk.CellRendererText, but it doesn't. It's not a different value for each row at that column, but the same value as setted in the Gtk.CellRendererCombo.Text
The Gtk.CellRenderer should not contain any state, so OK, using the Text field is a really bad idea from what I'm trying to do.
But if I set some value from the Gtk.ListStore that is the Model of my TreeView (Which is different from the Model for the Gtk.CellRendererCombo). The values setted will never show at the Column of the ComboBox.
class Program
{
private static Gtk.TreeView treeview = null;
static void OnEdited(object sender, Gtk.EditedArgs args)
{
Gtk.TreeSelection selection = treeview.Selection;
Gtk.TreeIter iter;
selection.GetSelected(out iter);
treeview.Model.SetValue(iter, 0, args.NewText); // the CellRendererCombo
treeview.Model.SetValue(iter, 1, args.NewText); // the CellRendererText
//(sender as Gtk.CellRendererCombo).Text = args.NewText; // Will set all the Cells of this Column to the Selection's Text
}
static void Main(string[] args)
{
Gtk.Application.Init();
Gtk.Window window = new Window("TreeView ComboTest");
window.WidthRequest = 200;
window.HeightRequest = 150;
Gtk.ListStore treeModel = new ListStore(typeof(string), typeof(string));
treeview = new TreeView(treeModel);
// Values to be chosen in the ComboBox
Gtk.ListStore comboModel = new ListStore(typeof(string));
Gtk.ComboBox comboBox = new ComboBox(comboModel);
comboBox.AppendText("<Please select>");
comboBox.AppendText("A");
comboBox.AppendText("B");
comboBox.AppendText("C");
comboBox.Active = 0;
Gtk.TreeViewColumn comboCol = new TreeViewColumn();
Gtk.CellRendererCombo comboCell = new CellRendererCombo();
comboCol.Title = "Combo Column";
comboCol.PackStart(comboCell, true);
comboCell.Editable = true;
comboCell.Edited += OnEdited;
comboCell.TextColumn = 0;
comboCell.Text = comboBox.ActiveText;
comboCell.Model = comboModel;
comboCell.WidthChars = 20;
Gtk.TreeViewColumn valueCol = new TreeViewColumn();
Gtk.CellRendererText valueCell = new CellRendererText();
valueCol.Title = "Value";
valueCol.PackStart(valueCell, true);
valueCol.AddAttribute(valueCell, "text", 1);
treeview.AppendColumn(comboCol);
treeview.AppendColumn(valueCol);
// Append the values used for the tests
treeModel.AppendValues("comboBox1", string.Empty); // the string value setted for the first column does not appear.
treeModel.AppendValues("comboBox2", string.Empty);
treeModel.AppendValues("comboBox3", string.Empty);
window.Add(treeview);
window.ShowAll();
Gtk.Application.Run();
}
}
I'd like the Cell of the ComboBox in which the selection has been made to show the value, but continue to be editable for later changes.
If someone has a way of doing this, I would be very grateful for you input. thanks.
Update:
What I think, because Gtk.CellRendererCombo inherits from Gtk.CellRendererText it just ignores the value setted in the cell. Now, I guess I could create a custom MyCellRendererCombo that inherits from Gtk.CellRendererCombo and use the value setted in the cell when supplied for the rendering, but the documentation on the difference between Gtk.CellRendererText and Gtk.CellRendererCombo is quite slim... I guess I should visit the implementation in C to know the details.
Ok, I've found a solution to my problem, I use a "hidden" column in which I read the "text" value. The hidden column contains the Gtk.CellRendererText, the Gtk.CellRendererCombo must have an Attribute Mapping with the new Column.
The resulting code is below:
class Program
{
private static Gtk.TreeView treeview = null;
static void OnEdited(object sender, Gtk.EditedArgs args)
{
Gtk.TreeSelection selection = treeview.Selection;
Gtk.TreeIter iter;
selection.GetSelected(out iter);
treeview.Model.SetValue(iter, 1, args.NewText); // the CellRendererText
}
static void Main(string[] args)
{
Gtk.Application.Init();
Gtk.Window window = new Window("TreeView ComboTest");
window.WidthRequest = 200;
window.HeightRequest = 150;
Gtk.ListStore treeModel = new ListStore(typeof(string), typeof(string));
treeview = new TreeView(treeModel);
// Values to be chosen in the ComboBox
Gtk.ListStore comboModel = new ListStore(typeof(string));
Gtk.ComboBox comboBox = new ComboBox(comboModel);
comboBox.AppendText("<Please select>");
comboBox.AppendText("A");
comboBox.AppendText("B");
comboBox.AppendText("C");
comboBox.Active = 0;
Gtk.TreeViewColumn comboCol = new TreeViewColumn();
Gtk.CellRendererCombo comboCell = new CellRendererCombo();
comboCol.Title = "Combo Column";
comboCol.PackStart(comboCell, true);
comboCol.AddAttribute(comboCell, "text", 1);
comboCell.Editable = true;
comboCell.Edited += OnEdited;
comboCell.TextColumn = 0;
comboCell.Text = comboBox.ActiveText;
comboCell.Model = comboModel;
comboCell.WidthChars = 20;
Gtk.TreeViewColumn valueCol = new TreeViewColumn();
Gtk.CellRendererText valueCell = new CellRendererText();
valueCol.Title = "Value";
valueCol.PackStart(valueCell, true);
valueCol.AddAttribute(valueCell, "text", 1);
valueCol.Visible = false;
treeview.AppendColumn(comboCol);
treeview.AppendColumn(valueCol);
// Append the values used for the tests
treeModel.AppendValues("comboBox1", "<Please select>"); // the string value setted for the first column does not appear.
treeModel.AppendValues("comboBox2", "<Please select>");
treeModel.AppendValues("comboBox3", "<Please select>");
window.Add(treeview);
window.ShowAll();
Gtk.Application.Run();
}
}
I have a textbox in xaml here is code in .cs file:
public static readonly DependencyProperty dp =
DependencyProperty.Register(
"result", typeof( uint ), typeof( ui ),
new FrameworkPropertyMetadata( ( uint )100, new PropertyChangedCallback( ResultChanged ) ) );
private static void ResultChanged(
DependencyObject d, DependencyPropertyChangedEventArgs e )
{
var input = ( ui )d;
var value = ( uint )e.NewValue;
}
Above code works great it is not allowing any alphabets or invalid characters to be entered in the textbox. But how can i change above code so that user will not be able to enter "0" in the textbox? So basically allow all uint except 0.
There are many ways to do this....the simplest and easiest is just to use the NumericUpDown control from the Extended WPF Toolkit....set the min/max range and you are sorted.
http://wpftoolkit.codeplex.com/wikipage?title=NumericUpDown
If you do want to do the logic all yourself then....
You could specify a "Coercion Callback" and/or a "Validate Callback" in the Register of the Dependency Property depending on your needs/requirements i.e. do you need to fix the value to a range, or show an error indicator to the user?
"Coercion" is where you adjust the value to something else e.g. in this case you might decide to adjust any value of 0, to 1 (or possibly null).
"Validation" is where the value is checked and you say if it is correct or not (by returning true of false)...if you return false...then an exception gets raised.
You use ValidatesOnException on a Binding to the DependencyProperty in order for that "error" to be propagated to the user interface in the way of display of an ErrorTemplate (the default one shows a red border around a control).
http://www.shujaat.net/2010/07/wpf-validation-validatevaluecallback.html
http://msdn.microsoft.com/en-us/library/ms745795.aspx
http://wpf.2000things.com/2010/11/11/122-validating-a-dependency-property/
http://wpf.2000things.com/2010/11/12/123-coercing-a-dependency-property/
http://wpf.2000things.com/2010/12/09/150-an-example-of-using-propertychanged-and-coercevalue-callbacks/
In this case I will show just using the Coercion Callback...as I'm presuming you don't want an invalid value to be waiting inside your TextBox. If you want the Validate one...then see the links above. (you don't need a Changed callback, so that's set to null).
public static readonly DependencyProperty dp =
DependencyProperty.Register(
"result", typeof( uint ), typeof( ui ),
new FrameworkPropertyMetadata(
( uint )100,
null,
new CoerceValueCallback( ResultCoerceValue )
)
);
private static object ResultCoerceValue
(DependencyObject depObj, object baseValue)
{
uint coercedValue = (uint)baseValue;
if ((uint)baseValue == 0)
coercedValue = 1; // might be able to set to null...but not sure on that.
return coercedValue;
}
There are also different techniques you can use like ValidationRules, possibly MaskedTextBox, and using PreviewTextInput.
http://social.msdn.microsoft.com/Forums/en/wpf/thread/990c635a-c14e-4614-b7e6-65471b0e0e26
How do I get a TextBox to only accept numeric input in WPF?
http://weblogs.asp.net/monikadyrda/archive/2009/06/24/wpf-textbox-validation.aspx
I'm inserting an item in my common control listview like this:
void InsertRow (HWND hWnd, char *col1, char *col2)
{
LV_ITEM lvItem;
lvItem.mask = 0;
lvItem.iItem = 0;
lvItem.iSubItem = 0;
lvItem.iItem = ListView_InsertItem (hWnd, &lvItem);
lvItem.mask = LVIF_TEXT;
lvItem.pszText = col1;
lvItem.cchTextMax = strlen (lvItem.pszText);
ListView_SetItem (hWnd, &lvItem);
lvItem.iSubItem = 1;
lvItem.pszText = col2;
lvItem.cchTextMax = strlen (lvItem.pszText);
ListView_SetItem (hWnd, &lvItem);
}
and works fine, but it is a pain because the vertical scroll goes back to the top of the list, so if I'm watching an item and call this insert function I lost my view, and have to scroll back manually, making my program impossible to work with.
How ca I prevent this autoscrolling?
I'm programming in C, with the win32 API directly (not MFC).
P.D.: ListView style:
LVS_SINGLESEL | WS_BORDER | WS_TABSTOP | WS_CHILD|WS_VISIBLE|WS_BORDER|WS_VSCROLL|WS_HSCROLL|LVS_REPORT
One way is to get the current top position, insert the new item, and then (programatically) scroll back to the saved top position.
Call ListView_GetItem to get the index of the item you want to watch
Call ListView_EnsureVisible(hWnd, index_you_want_to_watch, TRUE) every time you insert a new row.
If you can switch the ListView to virtual mode (apply the LVS_OWNERDATA style and then use the LVN_GETDISPINFO notification to supply your data to the ListView when requested), then you can use ListView_SetItemCountEx() to add/insert/remove items. It has a LVSICF_NOSCROLL flag to prevent scrolling.
I'm developing a Windows Forms application in VS2008. I want to display a unknown, but small number of DataGridViews on a form, using code like this:
foreach (QueryFilter f in Query.Filter)
{
DataGridView grid = CreateGridView(String.Format("GridView{0}", filters.Count));
grid.Location = new System.Drawing.Point(3, 9 + (filters.Count * grid.Height + 9));
BindingList<QueryFilterNode> nodes = new BindingList<QueryFilterNode>();
foreach (QueryFilterNode node in f)
nodes.Add(node);
grid.DataSource = nodes;
panel1.Controls.Add(grid);
filters.Add(nodes);
}
The grid(s) are added to the panel, but the data inside is not displayed. My guess is setting the DataSource property doesn't actualy bind the grid, because (for example) the dataGridView_ColumnAdded event is not fired.
QueryFilter and QueryFilterNode are just POCO's and contain data of course.
For completeness sake the construction of the DataGridView:
private DataGridView CreateGridView(string name)
{
DataGridView grid = new DataGridView();
grid.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
grid.Name = name;
grid.Size = new System.Drawing.Size(484, 120);
grid.ColumnAdded += new System.Windows.Forms.DataGridViewColumnEventHandler(this.dataGridView_ColumnAdded);
return grid;
}
Hmm, it seems it was my own mistake.
QueryFilterNode, used as datasource ( BindingList<QueryFilterNode> ) wasn't a POCO but a datacontract. Snippet:
[DataContract(Name = "QueryFilterNode")]
public class QueryFilterNode
{
[DataMember(IsRequired = true)]
public string FieldCode;
For some reason these cannot be databound. I used a simple class like this in my BindingList and it just worked.
class QueryFilterNodeSimple
{
public string FieldCode
{ get; set; }