I feel that it's something elementary but I can't find out how to add to class commands for PropertyGrid.
There is a sample class (the real classes are much more complex, but I need only the simplest example to implement the required functionality)
Public Class SampleClass
Public Const DefValue = 1
Public Property Value1 As Integer = DefValue
Public Property Value2 As Integer = DefValue
Public Sub Reset()
Value1 = DefValue
Value2 = DefValue
End Sub
End Class
Used In the sample form:
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()>
Partial Class Form1
Inherits System.Windows.Forms.Form
'Form overrides dispose to clean up the component list.
<System.Diagnostics.DebuggerNonUserCode()>
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
Try
If disposing AndAlso components IsNot Nothing Then
components.Dispose()
End If
Finally
MyBase.Dispose(disposing)
End Try
End Sub
'Required by the Windows Form Designer
Private components As System.ComponentModel.IContainer
'NOTE: The following procedure is required by the Windows Form Designer
'It can be modified using the Windows Form Designer.
'Do not modify it using the code editor.
<System.Diagnostics.DebuggerStepThrough()>
Private Sub InitializeComponent()
Me.PropertyGrid1 = New System.Windows.Forms.PropertyGrid()
Me.SuspendLayout()
'
'PropertyGrid1
'
Me.PropertyGrid1.Location = New System.Drawing.Point(242, 98)
Me.PropertyGrid1.Name = "PropertyGrid1"
Me.PropertyGrid1.Size = New System.Drawing.Size(294, 250)
Me.PropertyGrid1.TabIndex = 0
'
'Form1
'
Me.AutoScaleDimensions = New System.Drawing.SizeF(7.0!, 15.0!)
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
Me.ClientSize = New System.Drawing.Size(800, 450)
Me.Controls.Add(Me.PropertyGrid1)
Me.Name = "Form1"
Me.Text = "Form1"
Me.ResumeLayout(False)
End Sub
Friend WithEvents PropertyGrid1 As PropertyGrid
End Class
An instance is attached to PropertyGrid by the next piece of code:
Public Class Form1
Private Sample As New SampleClass
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
PropertyGrid1.SelectedObject = Sample
End Sub
End Class
The goal is to have a link that resets the contents of the instance at bottom of PropertyGrid like in the picture below, I presume that some attribute for a class is required but can't find what attribute.
Thanks for any advice.
Related
I want to create an editable table and it size is defined by user (size can be 10*10 or 20*20 or 30*30).
--
I found this topic (here) but it's running in WinForms, and the DataGridView is not supported by WPF.
I tried with a DataGrid, but the following row doesn't working :
Me.DataGridTableau.ItemsSource = dt
--
I tried with a RadGridView (Telerik) but rows are only updatable by ItemsSource property, and like I don't know how many columns will be, I can't create an object which represents the table (x properties for x columns).
Can anybody help me?
You can set the ItemsSource of a DataGrid to any IEnumerable, including a DataView of a DataTable:
Me.DataGridTableau.ItemsSource = dt.DefaultView
If anybody need it, I found a solution using a RadGridView (Telerik) :
Create this class :
Imports System.Dynamic
Imports System.Collections.Generic
Imports System.ComponentModel
Public Class MyDataRow
Inherits DynamicObject
Implements INotifyPropertyChanged
ReadOnly data As IDictionary(Of String, Object)
Public Sub New()
data = New Dictionary(Of String, Object)()
End Sub
Public Sub New(ByVal source As IDictionary(Of String, Object))
data = source
End Sub
Public Overrides Function GetDynamicMemberNames() As IEnumerable(Of String)
Return data.Keys
End Function
Public Overrides Function TryGetMember(ByVal binder As GetMemberBinder, ByRef result As Object) As Boolean
result = Me(binder.Name)
Return True
End Function
Public Overrides Function TrySetMember(ByVal binder As SetMemberBinder, ByVal value As Object) As Boolean
Me(binder.Name) = value
Return True
End Function
Default Public Property Item(ByVal columnName As String) As Object
Get
If data.ContainsKey(columnName) Then
Return data(columnName)
End If
Return Nothing
End Get
Set(ByVal value As Object)
If Not data.ContainsKey(columnName) Then
data.Add(columnName, value)
OnPropertyChanged(columnName)
Else
If data(columnName) <> value Then
data(columnName) = value
OnPropertyChanged(columnName)
End If
End If
End Set
End Property
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Protected Sub OnPropertyChanged(name As String)
Try
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(name))
Catch
If Debugger.IsAttached Then Debugger.Break()
Throw ' rethrow exception
End Try
End Sub
Protected Sub OnPropertyChanged(event_args As PropertyChangedEventArgs)
RaiseEvent PropertyChanged(Me, event_args)
End Sub
End Class
In your VM, you need a public property :
Private _tableau As ObservableCollection(Of MyDataRow)
Public Property Tableau() As ObservableCollection(Of MyDataRow)
Get
Return _tableau
End Get
Set(ByVal value As ObservableCollection(Of MyDataRow))
_tableau = value
OnPropertyChanged("Tableau")
End Set
End Property
You need to create a method to load your table :
Private Function LoadTableau() As ObservableCollection(Of MyDataRow)
Dim taille As Integer = Me.GetTailleTableau()
If taille = 0 Then Return Nothing
Dim data As New ObservableCollection(Of MyDataRow)()
For i As Integer = 0 To (taille - 1)
Dim row = New MyDataRow()
For j As Integer = 0 To (taille - 1)
'row(String.Format("Column{0}", j)) = String.Format("Cell {0} {1}", i, j)
row(j) = ""
Next
data.Add(row)
Next
Return data
End Function
You need to load your table :
Me.Tableau = Me.LoadTableau()
And you need to bind your table :
<telerik:RadGridView x:Name="RadGridViewTableau" ItemsSource="{Binding Tableau}" >
I hope this help :)
I'm using vb.net framework 4.5 and WPF project.
I have a button, the function adds a certain product info to a datagrid. In my vb code file I set a product class
Public Class MyProduct
Public Property ItemNumber As String
Public Property ItemDescription As String
Public Property ItemUnitPrice As Double
Public Property ItemQty As Integer
End Class
The button touchdown event
Private Sub Button_TouchDown(sender As Object, e As TouchEventArgs)
Dim dmb As New MyProduct
dmb.ItemNumber = "abc001"
dmb.ItemDescription = "bla bla bla"
dmb.ItemQty = 1
dmb.ItemUnitPrice = 123.45
MyDataGrid.Items.Add(dmb)
End Sub
Currently, if I tap multiple times of this button, the data grid will add multiple duplicated rows for same product. My goal is when multiple same product add to datagrid, only one row shows and each additional tap/click action on the same button will only increase the ItemQty number.
How can I do that? Thanks!
First, you need to prevent inserting twice :
Private Sub buttonAdd_Click(sender As Object, e As RoutedEventArgs) Handles buttonAdd.Click
Dim dmb As New MyProduct
dmb.ItemNumber = New Random().Next(5).ToString()
dmb.ItemDescription = "bla bla bla"
dmb.ItemQty = 1
dmb.ItemUnitPrice = 123.45
Dim dmbSearched As MyProduct = Nothing
For Each dmbs As MyProduct In MyDataGrid.Items
If dmbs.ItemNumber = dmb.ItemNumber Then
dmbSearched = dmbs
Exit For
End If
Next
If dmbSearched Is Nothing Then
MyDataGrid.Items.Add(dmb)
Else
dmbSearched.ItemQty += 1
End If
End Sub
Second the MyProduct class must raise an event when the quantity is changed, otherwise there is no visible change :
Public Class MyProduct : Implements INotifyPropertyChanged
Private Property m_ItemQty As Integer
Public Property ItemQty As Integer
Get
Return m_ItemQty
End Get
Set(value As Integer)
m_ItemQty = value
FirePropertyChanged()
End Set
End Property
Public Sub FirePropertyChanged(<CallerMemberName> Optional propName As String = "")
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propName))
End Sub
Public Event PropertyChanged(sender As Object, e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged
Public Property ItemNumber As String
Public Property ItemDescription As String
Public Property ItemUnitPrice As Double
End Class
Regards
I have a WPF application I am working on that uses the Entity Framework 4.0 an Observable Collection and a few CollectionViewSources. On my form i have two text boxes i use to filter my data and then display it into a listbox based on what I have filtered it by. What I need to do is to have the user select an item from the listbox and hit a button ("Select") and the information is displayed wiht appropriate collections in a whole new window.
So far I have:
Imports System.Data.Objects
Public Class SearchText
Private db As New CraftingProjectsEntities
Private ProjectsData As ProjectsCollection
Private ProjectViewSource As CollectionViewSource
Private MaterialsViewSource As CollectionViewSource
Private ColoringsViewSource As CollectionViewSource
Private DirectionsViewSource As CollectionViewSource
Private SpecialsViewSource As CollectionViewSource
Private WithEvents ProjectView As ListCollectionView
Private MaterialsView As BindingListCollectionView
Private ColoringsView As BindingListCollectionView
Private SpecialsView As BindingListCollectionView
Private DirectionsView As BindingListCollectionView
Private Sub SearchText_Loaded(sender As Object, e As System.Windows.RoutedEventArgs) Handles Me.Loaded
Dim query = From r In db.Projects
Where r.Materials.Count > 1
Select r
Me.ProjectViewSource = CType(Me.FindResource("ProjectsViewSource"), CollectionViewSource)
Me.DirectionsViewSource = CType(Me.FindResource("DirectionsViewSource"), CollectionViewSource)
Me.ColoringsViewSource = CType(Me.FindResource("ColoringsViewSource"), CollectionViewSource)
Me.SpecialsViewSource = CType(Me.FindResource("SpecialsViewSource"), CollectionViewSource)
Me.MaterialsViewSource = CType(Me.FindResource("MaterialsViewSource"), CollectionViewSource)
Me.ProjectViewSource.Source = Me.ProjectsData
Me.ProjectView = CType(Me.ProjectViewSource.View, ListCollectionView)
Me.DirectionsView = CType(Me.DirectionsViewSource.View, BindingListCollectionView)
Me.ColoringsView = CType(Me.ColoringsViewSource.View, BindingListCollectionView)
Me.MaterialsView = CType(Me.MaterialsViewSource.View, BindingListCollectionView)
Me.SpecialsView = CType(Me.SpecialsViewSource.View, BindingListCollectionView)
MyLists.ItemsSource = ProjectsData
End Sub
Private Sub Button1_Click(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles Button1.Click
Dim query2 As ObjectQuery(Of Project) = Nothing
Dim query = From p In db.Projects
Select p
If TextBox2.Text.Length > 1 And TextBox1.Text.Length > 1 Then
query2 = query.Where(Function(p) p.Materials.Any(Function(m) m.Material1.ToLower().Contains(Me.TextBox1.Text)) And
p.Materials.Any(Function(m) m.Material1.ToLower().Contains(Me.TextBox2.Text)))
ElseIf TextBox2.Text.Length < 1 And TextBox1.Text.Length > 1 Then
query2 = query.Where(Function(p) p.Materials.Any(Function(m) m.Material1.ToLower().Contains(Me.TextBox1.Text)))
Else
MessageBox.Show("What are you looking for?")
Return
End If
Me.ProjectsData = New ProjectsCollection(query2.ToList(), db)
MyLists.ItemsSource = ProjectsData
End Sub
**Works great through here... Below is where i get lost trying to get the selected item to the new window**
Private Sub btnSelect_Click(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles btnSelect.Click
Dim mychoice As New MyChoice
Dim selitem = Me.MyLists.SelectedItem
mychoice.ProjectsData = (selitem)
mychoice.Show()
Me.Close()
End Sub
End Class
Any thoughts on how I could do this or where I could find an example showing the proper way of doing this would be extremely appreciated. Thanks.
Adam
Sorry.... not using MVVM if this helps.
Make your model Project a parameter of the MyChoice window class. Depending on whether it's required for the window, make Project a required or optional parameter of the MyChoice constructor. Set it as the Data Context for your new window's Layout Root, set it as a property of the Window class itself, or put it in the View Model; whichever is appropriate.
I have a datagrid in my vb.net 4.0 wpf project. I have seen many examples in XAML on how to bind a DataGridComboBoxColumn however I need to do this in code as I have auto-generating columns. I switch the datagridbinding source to multiple data sets.
Inside some of these custom classes are a couple lists. I can get text and checkboxes to auto-generate correctly. When it comes across my array (I have tried many different kinds) I just see a textboxcolumn with the words (Collection) in it.
For instance - One screen I am grabbing system information on WMI calls. One of the calls returns all IP addresses on the server (We can have up to 8 IP addresses) I do not want a column per IP address. I would like to include a list of those into the datagrid so you can drop down and see them.
Any suggestions on if this is possible or if I am doing something wrong?
Thank you
Sample Code
Imports System.Collections.ObjectModel
Class MainWindow
Dim ServerInfoArray As ObservableCollection(Of ServerInfo) = New ObservableCollection(Of ServerInfo)
Private ReadOnly _ipAddresses As ObservableCollection(Of String) = New ObservableCollection(Of String)
Private Sub GetInfo(ByVal list As List(Of String))
For Each server As String In list
Dim tempip As List(Of String) = New List(Of String)
Dim sinfo As ServerInfo = New ServerInfo
tempip.Add("192.129.123.23")
tempip.Add("23.213.223.21")
sinfo.IPArray = tempip
sinfo.Servername = server
ServerInfoArray.Add(sinfo)
Next
End Sub
Private Sub Button1_Click(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles Button1.Click
Dim serverlist As List(Of String) = New List(Of String)
serverlist.Add("Test")
serverlist.Add("Random")
serverlist.Add("Local")
GetInfo(serverlist)
End Sub
Private Sub Window_Loaded(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded
Dim Col_Serial As DataGridTextColumn = New DataGridTextColumn()
Col_Serial.Binding = New Binding("Servername")
Col_Serial.Header = "Servername"
Col_Serial.Width = 40
Dim Col_IPArray = New DataGridComboBoxColumn()
Col_IPArray.Header = "IP Addresses"
Col_IPArray.IsReadOnly = True
Col_IPArray.ItemsSource = Me._ipAddresses
Col_IPArray.SelectedItemBinding = New Binding("IPArray")
DataGrid1.Columns.Add(Col_Serial)
DataGrid1.Columns.Add(Col_IPArray)
DataGrid1.ItemsSource = ServerInfoArray
End Sub
End Class
Class ServerInfo
Dim _Servername As String
Dim _IPArray As List(Of String)
Public Property Servername() As String
Get
Return _Servername
End Get
Set(ByVal value As String)
_Servername = value
End Set
End Property
Public Property IPArray As List(Of String)
Get
Return _IPArray
End Get
Set(ByVal value As List(Of String))
_IPArray = value
End Set
End Property
Public Sub New()
_Servername = Nothing
_IPArray = New List(Of String)
End Sub
End Class
Not sure if I am understanding perfectly but if you can live with AutoGenerateColumns = False then you can manipulate the columns, their properties and bindings from code like below. So you could use whatever logic you need to in order setup the columns from code. I tried to show how you can source the combobox column items from a different object than the items in the DataGrid as well.
This is meant to be a simple example so I just did everything in the code-behind but from an MVVM standpoint, it depends on your preferred approach but possibility is that you could accomplish the same logic from a controller class that spins up your view models, etc...
Hope it helps!
XAML...
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<Button x:Name="btnRefreshIPList">Refresh list of IPs</Button>
<DataGrid x:Name="dataGrid1"></DataGrid>
</StackPanel>
</Window>
Code behind...
Imports System.Collections.ObjectModel
Class MainWindow
'The list of IPs for column's ItemSource property
Private ReadOnly _ipAddresses As ObservableCollection(Of String)
'The items for binding to the DataGrid's ItemsSource
Private _items As List(Of MyObjectWithIPAddress)
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
_ipAddresses = New ObservableCollection(Of String)
_items = New List(Of MyObjectWithIPAddress)
End Sub
Private Sub Window_Loaded(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded
Me.dataGrid1.AutoGenerateColumns = False
dataGrid1.Columns.Clear()
'Example of text column (Text bound to Name property)
Dim dgTxtCol = New DataGridTextColumn()
dgTxtCol.Header = "Name"
dgTxtCol.Binding = New Binding("Name")
dataGrid1.Columns.Add(dgTxtCol)
'Example of combobox column (SelectedItem bound to IPAddress)
Dim dgCmbCol = New DataGridComboBoxColumn()
dgCmbCol.Header = "IP Address"
dgCmbCol.ItemsSource = Me._ipAddresses
dgCmbCol.SelectedItemBinding = New Binding("IPAddress")
dataGrid1.Columns.Add(dgCmbCol)
'Add items to DataGrid
_items.Add(New MyObjectWithIPAddress("foo1"))
_items.Add(New MyObjectWithIPAddress("foo2"))
Me.dataGrid1.ItemsSource = Me._items
End Sub
''' <summary>
''' To emulate fetching the object that has the IP list
''' </summary>
''' <returns></returns>
''' <remarks></remarks>
Private Function GetIpList() As MyObjectWithListOfIPs
Dim inst = New MyObjectWithListOfIPs
inst.IPList = New List(Of String)(New String() {"10.0.0.1", "10.0.0.2", "10.0.0.3"})
Return inst
End Function
''' <summary>
''' Updates the ObservableCollection instance based on business object
''' </summary>
''' <remarks></remarks>
Private Sub RefreshIpAddresses()
_ipAddresses.Clear()
For Each ip As String In GetIpList().IPList
_ipAddresses.Add(ip)
Next
End Sub
Private Sub btnRefreshIPList_Click(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles btnRefreshIPList.Click
RefreshIpAddresses()
End Sub
End Class
''' <summary>
''' Object with response data (e.g., list of IPs)
''' </summary>
''' <remarks></remarks>
Class MyObjectWithListOfIPs
Private _ipList As List(Of String)
Public Property IPList() As List(Of String)
Get
Return _ipList
End Get
Set(ByVal value As List(Of String))
_ipList = value
End Set
End Property
End Class
''' <summary>
''' Disperate object that "has an" address
''' </summary>
''' <remarks></remarks>
Class MyObjectWithIPAddress
Public Sub New(name As String)
Me._name = name
End Sub
Private _name As String
Public Property Name() As String
Get
Return _name
End Get
Set(ByVal value As String)
_name = value
End Set
End Property
Private _ipAddress As String
Public Property IPAddress() As String
Get
Return _ipAddress
End Get
Set(ByVal value As String)
_ipAddress = value
End Set
End Property
End Class
I'm writing a custom textblock control thats populate hyperlinks and raises event when clicked to hyperlink.
I wrote this code but I got stucked.
My code is :
Imports System.Text.RegularExpressions
Public Class CustomTextBlock
Inherits TextBlock
Public Event Klik As EventHandler(Of EventArgs)
Public ReadOnly InlineCollectionProperty As DependencyProperty = DependencyProperty.Register("InlineCollection", GetType(String), GetType(CustomTextBlock), New PropertyMetadata(New PropertyChangedCallback(AddressOf CustomTextBlock.InlineChanged)))
Private Shared Sub InlineChanged(ByVal sender As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
DirectCast(sender, CustomTextBlock).Inlines.Clear()
Dim kelimeler = Split(e.NewValue, " ")
For i = 0 To kelimeler.Length - 1
If Regex.Match(kelimeler(i), "(http|ftp|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,#?^=%&:/~\+#]*[\w\-\#?^=%&/~\+#])?").Success Then
Dim x = New Hyperlink(New Run(kelimeler(i)))
x.AddHandler(Hyperlink.ClickEvent, New RoutedEventHandler(AddressOf t_Click))
x.ToolTip = kelimeler(i)
x.Tag = kelimeler(i)
DirectCast(sender, CustomTextBlock).Inlines.Add(x)
If Not i = kelimeler.Length Then DirectCast(sender, CustomTextBlock).Inlines.Add(" ")
Else
DirectCast(sender, CustomTextBlock).Inlines.Add(kelimeler(i))
If Not i = kelimeler.Length Then DirectCast(sender, CustomTextBlock).Inlines.Add(" ")
End If
''//Console.WriteLine(kelime(i).ToString.StartsWith("#"))
Next
kelimeler = Nothing
End Sub
Public Property InlineCollection As String
Get
Return DirectCast(GetValue(InlineCollectionProperty), String)
End Get
Set(ByVal value As String)
SetValue(InlineCollectionProperty, value)
End Set
End Property
Private Shared Sub t_Click(ByVal sender As Hyperlink, ByVal e As System.Windows.RoutedEventArgs)
e.Handled = True
RaiseEvent Klik(sender, EventArgs.Empty)
End Sub
End Class
This code gives error at RaiseEvent Klik(sender, EventArgs.Empty)
Error is : Cannot refer to an instance member of a class from within a shared method or shared member initializer without an expliticit instance of the class.
Thanks for your answers,
Alper
The problem is clearly stated in the exception message. The t_Click method is Shared (which means common to all instances of the class), so it cannot raise an Event that is specific to an instance of the class. You should only raise the event from a method that is not shared.
Do something like this -
Imports System
Imports System.Text.RegularExpressions
Public Class CustomTextBlock
Inherits TextBlock
Public Event Klik As EventHandler(Of System.EventArgs)
Public ReadOnly InlineCollectionProperty As DependencyProperty = DependencyProperty.Register("InlineCollection", GetType(String), GetType(CustomTextBlock), New PropertyMetadata(New PropertyChangedCallback(AddressOf CustomTextBlock.InlineChanged)))
Private Shared Sub InlineChanged(ByVal sender As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
Dim d As CustomTextBlock = DirectCast(sender, CustomTextBlock)
d.Inlines.Clear()
d.OnInlineChanged(CType(e.NewValue, String))
End Sub
Private Sub OnInlineChanged(ByVal Value As String)
Dim kelimeler = Split(Value, " ")
For i As Integer = 0 To kelimeler.Length - 1
If Regex.Match(kelimeler(i), "(http|ftp|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,#?^=%&:/~\+#]*[\w\-\#?^=%&/~\+#])?").Success Then
Dim x = New Hyperlink(New Run(kelimeler(i)))
x.AddHandler(Hyperlink.ClickEvent, New RoutedEventHandler(AddressOf t_Click))
x.ToolTip = kelimeler(i)
x.Tag = kelimeler(i)
Me.Inlines.Add(x)
If Not i = kelimeler.Length Then Me.Inlines.Add(" ")
Else
Me.Inlines.Add(kelimeler(i))
If Not i = kelimeler.Length Then Me.Inlines.Add(" ")
End If
''//Console.WriteLine(kelime(i).ToString.StartsWith("#"))
Next
kelimeler = Nothing
End Sub
Public Property InlineCollection As String
Get
Return DirectCast(GetValue(InlineCollectionProperty), String)
End Get
Set(ByVal value As String)
SetValue(InlineCollectionProperty, value)
End Set
End Property
Private Sub t_Click(ByVal sender As Hyperlink, ByVal e As System.Windows.RoutedEventArgs)
e.Handled = True
RaiseEvent Klik(sender, System.EventArgs.Empty)
End Sub
End Class