diff --git a/docs/docs/Adding data to your TableView/Assigning-Columns-From-A-Collection.md b/docs/docs/Adding data to your TableView/Assigning-Columns-From-A-Collection.md new file mode 100644 index 0000000..825e0fb --- /dev/null +++ b/docs/docs/Adding data to your TableView/Assigning-Columns-From-A-Collection.md @@ -0,0 +1,485 @@ +> [🔙 **Back to *Adding data to your TableView***](Data-To-TableView.md) +# Adding columns from a collection +This approach lets you add a predetermined set of columns with their own data that can be changed and added to dynamically. + +| | This method lets you | +| --- | -------------------------------------- | +| ✅ | Display a TableView with its own data | +| ✅ | Edit and access the TableView's data | +| ✅ | Create new rows dynamically | +| ❌ | Create new custom columns dynamically | +| ❌ | Assign new dynamic data to new columns | + +#### Good for things like: +- An ingredients list +- A contact list where the user can add new contacts and assign them data in columns like name, phone number, email, etc. + +## Before you begin: **Understanding TableView columns** +There are different types of TableView columns like `TableViewTextColumn`, `TableViewCheckBoxColumn`, `TableViewComboBoxColumn` and more. + +When dynamically adding columns, some columns can have their type assigned automatically. For example, if you have an `int` value for a column the column will automatically become a `TableViewNumberColumn`. The same goes for a TextColumn with `string` and CheckBoxColumn with `bool`. Columns are auto-generated if [**`AutoGenerateColumns`**](..\AutoGenerateColumns.md) is set to `true` + +TableView columns are often stored in their own classes. These are collections (the TableView interprets them as `TableViewColumnsCollection`) where you can store data for one or multiple columns. This is really useful when adding columns dynamically from code-behind and assigning them custom or dynamic values. + +### About `ObservableCollection` and the `INotifyPropertyChanged` class +The data shown by a TableView is typically bound to an `ObservableCollection` where `T` is your item type (for example, `Person`). `Person`, in this case, is a **class** that stores all the values (and therefore columns) for a person like `Name`, `IsActive` and `Age`. That class is bound to an `ObservableCollection` which can be used to reference the items and create a new instance of that class to add the columns when needed. + +To simplify, there are two steps to this: +1. Create a class, where you store all your values (or columns) +2. Declare that class in an `ObservableCollection` so you can use it and add it over and over dynamically + +#### 1. Creating the class +If you want edits to update the UI immediately, the item type should implement `INotifyPropertyChanged`. +Let's say we have this class, which is what you would normally expect to write: +```csharp +public class Person +{ + public string Name { get; set; } + public bool IsActive { get; set; } + public int Age { get; set; } +} +``` +⬆️ However, that doesn't use `INotifyPropertyChanged`, so nothing changes if the values are changed and doesn't work with TableView. + +Here is how the item class would look if you implement `INotifyPropertyChanged` so edits are reflected in the UI: +```csharp +public class Person : INotifyPropertyChanged +{ + private string _name; + private bool _isActive; + private int _age; + + public string Name + { + get => _name; + set { _name = value; OnPropertyChanged(nameof(Name)); } + } + + public bool IsActive + { + get => _isActive; + set { _isActive = value; OnPropertyChanged(nameof(IsActive)); } + } + + public int Age + { + get => _age; + set { _age = value; OnPropertyChanged(nameof(Age)); } + } + + public event PropertyChangedEventHandler? PropertyChanged; + protected void OnPropertyChanged(string propertyName) => + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); +} +``` + +#### Here is a copy-ready class template to edit for your own data: +```csharp +public class MyItem : INotifyPropertyChanged +{ + private _; + + public + { + get => _; + set { _ = value; OnPropertyChanged(nameof()); } + } + + // Repeat the above pattern for each property you need. + + public event PropertyChangedEventHandler? PropertyChanged; + protected void OnPropertyChanged(string propertyName) => + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); +} +``` +Leave everything outside of `<>` unchanged. + + +## Creating TableView columns +There are two ways to add columns to your TableView *using this approach*: +1. **Auto-Generated columns** for text, numbers, date and time, and boolean values +2. **Predefined custom columns** using XAML. +3. **Custom columns** for more control over the column type and values (ComboBox, etc.) + +### 1. Auto-Generated columns +If you bind a list of objects (like an `ObservableCollection`) to a TableView, it will automatically create a column for each public property on the item type. The type of column depends on the property type: +- `string` → Text column +- `bool` → Checkbox column +- `int`, `double`, etc. → Number column +- `DateOnly` → Date column +- `TimeOnly` → Time column + +**You do not need to do anything special for these types.** + +> [!TIP] +> Read more about Auto-Generated columns [**here**](docs\AutoGenerateColumns.md) + +To add the items, in your page or viewmodel class, first declare your `ObservableCollection`. This has all the variables from the `People` collection from before: +```csharp +public ObservableCollection People { get; } = new(); +``` + +and then add items to it: +```csharp +People.Add(new Person { Name = "George", Age = 30, IsActive = true }); +``` + +Finally, make your TableView is bound to your collection: +```xml + +``` + +If you bind a list of `Person` to the TableView, it will show three columns: Name (text), IsActive (checkbox), and Age (number). These columns are created automatically based on the property types. + +
+ +Full code example + +In your XAML: +```xml + + + + +``` + +In C# (Using MainWindow as an example here): +```cs +// This is the Person class, where all the data of a single person is stored. Think of this as its own row, and the variables are its own items or columns. +public class Person : INotifyPropertyChanged +{ + private string _name; + private bool _isActive; + private int _age; + + public string Name + { + get => _name; + set { _name = value; OnPropertyChanged(nameof(Name)); } + } + + public bool IsActive + { + get => _isActive; + set { _isActive = value; OnPropertyChanged(nameof(IsActive)); } + } + + public int Age + { + get => _age; + set { _age = value; OnPropertyChanged(nameof(Age)); } + } + + public event PropertyChangedEventHandler? PropertyChanged; + protected void OnPropertyChanged(string propertyName) => + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); +} + +public sealed partial class MainWindow : Window // This could also be a page. Doesn't really matter. +{ + public ObservableCollection People { get; } = new(); // Declare a new ObserableCollection of the type Person so you can use it in code to add columns. + + public MainWindow() + { + InitializeComponent(); + + // You can put this code anywhere, for example in a function. It's just here for the sake of simplicity. + // This creates a new row for a Person with the new columns of Name, Age, and IsActive. + People.Add(new Person { Name = "George", Age = 30, IsActive = true }); + } +} +``` +
+ +----- + +### 2. Predefining columns using XAML +This is the most straightforward way to do this that doesn't involve changing the `AutoGeneratingColumn` event. This involves setting the columns in XAML and binding them to their own data. + +> [!CAUTION] +> You will need to disable `AutoGenerateColumns` for this. +> ```xml +> +> ``` + +#### 1. Have your data ready. +This class sets the data for just one column. +```csharp +public class Price : INotifyPropertyChanged +{ + private int _price; + + public int Price + { + get => _price; + set { _price = value; OnPropertyChanged(nameof(Price)); } + } + + public event PropertyChangedEventHandler? PropertyChanged; + protected void OnPropertyChanged(string propertyName) => + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); +} +``` + +#### 2. Declare your class so you can use it. +Declare your class in your page or view model's class so you can bind it: +```csharp +public Price price = new Price(); +``` + +#### 3. Create the column in XAML +```xml + + + + + +``` + +
+Full XAML example + +```xml + + + + + + + + + + + + + + + + + + +``` +
+ +----- + +### 3. Customizing or Replacing Columns (ComboBox, ToggleSwitch, Template, etc.) + +Some columns, like ComboBox or custom templates, require extra steps. TableView does **not** automatically create a ComboBox just because you have a list or set of values. You must tell TableView to use a ComboBox column for a property. + +This is done by handling the `AutoGeneratingColumn` event. In this event you can: +- Change the type of column (e.g., from text to ComboBox) +- Set extra properties (like ItemsSource for ComboBox) + +### Example: Customizing a Column to be a ComboBox +Suppose you have a property called `Gender` and you want it to be a ComboBox with a list of genders. + +##### 1. Define your model: +Add the `Gender` property to your item type (or create your own item type similar to `Person` above), just like before: +```csharp +private string _gender; +public string Gender +{ + get => _gender; + set { _gender = value; OnPropertyChanged(nameof(Gender)); } +} +``` + +##### 2. Create a ViewModel +A ViewModel is a class that you can use to manage your data and dynamically add items to your TableView, allowing for more advanced options: +```cs +public class MainViewModel +{ + public ObservableCollection Genders { get; } = new ObservableCollection { "Male", "Female" }; + + public MainViewModel() + { + Tasks = new ObservableCollection + { + new Person { Name = "George", Age = 30, IsActive = true, Gender = "Male" } + }; + } +} +``` + +> [!TIP] +> You can also make changes or add items to your collection later like this: +> ```cs +> var viewModel = MyTableView.DataContext as MainViewModel; +> viewModel.Genders.Add("NewGender"); +> ``` + +##### 3. Declare your ViewModel so you can use it in your page: +```cs + public MainViewModel ViewModel { get; } = new MainViewModel(); +``` + +##### 4. Handle the AutoGeneratingColumn event: +This event is called for each property when TableView creates columns. You can replace the default column with a ComboBox column. Use safe casts and obtain your viewmodel from the TableView's DataContext so the handler works in other projects. + +```csharp +// Assign this method to the TableView.AutoGeneratingColumn event +void OnAutoGeneratingColumns(object sender, TableViewAutoGeneratingColumnEventArgs e) +{ + if (sender is not TableView tableView) return; + + // Try to get your view model from the TableView's DataContext + var viewModel = tableView.DataContext as MainViewModel; // replace MainViewModel with your VM type + + // The column that TableView would have used for this property + if (e.Column is not TableViewBoundColumn originalColumn) return; + + // Replace Gender column with your ComboBox column + if (e.PropertyName == nameof(Person.Gender) && viewModel != null) + { + e.Column = new TableViewComboBoxColumn + { + // Keep the same binding so the column still reads/writes the Gender property + Binding = originalColumn.Binding, + Header = originalColumn.Header, + + // Set the source for the options to the Genders collection (so the options for this example will be "male", "female", etc.) + ItemsSource = viewModel.Genders + }; + } +} +``` + +##### 5. Bind everything in your TableView: +```xml + +``` + +**Explanation:** +- `originalColumn.Binding` keeps the new column bound to the same property on each row item (e.g., `Person.Gender`). +- `ItemsSource` is the collection of allowed values shown in the ComboBox. It is independent from the cell Binding. +- Use the TableView's DataContext to access your ViewModel so the handler adapts to other projects. + +
+Full code example + +In your XAML: +```xml + + +``` + +In C#: +```cs +public class Person : INotifyPropertyChanged +{ + private string _name; + private bool _isActive; + private int _age; + private string _gender = "None Selected"; + + public string Name + { + get => _name; + set { _name = value; OnPropertyChanged(nameof(Name)); } + } + + public bool IsActive + { + get => _isActive; + set { _isActive = value; OnPropertyChanged(nameof(IsActive)); } + } + + public int Age + { + get => _age; + set { _age = value; OnPropertyChanged(nameof(Age)); } + } + + public string Gender + { + get => _gender; + set { _gender = value; OnPropertyChanged(nameof(Gender)); } + } + + public event PropertyChangedEventHandler? PropertyChanged; + protected void OnPropertyChanged(string propertyName) => + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); +} + +public class MainViewModel // This is our ViewModel class +{ + public ObservableCollection People { get; set; } // Declare your Person class so you can use it here to add items + public ObservableCollection Genders { get; } = new ObservableCollection { "Male", "Female" }; // Creates the Genders collection and adds the options + + public MainViewModel() + { + People = new ObservableCollection + { + new Person { Name = "George", Age = 30, IsActive = true } // Adds an item with these values. You can duplicate this to add more itesm + }; + } +} + +public sealed partial class MainWindow : Window // This could also be a page. Doesn't really matter. +{ + public MainViewModel ViewModel { get; } = new MainViewModel(); // Declare your ViewModel here so you can use it + + public MainWindow() + { + InitializeComponent(); + } + + void OnAutoGeneratingColumns(object sender, TableViewAutoGeneratingColumnEventArgs e) + { + if (sender is not TableView tableView) return; + + // Try to get your view model from the TableView's DataContext + var viewModel = tableView.DataContext as MainViewModel; // replace MainViewModel with your VM type + + // The column that TableView would have used for this property + if (e.Column is not TableViewBoundColumn originalColumn) return; + + // Replace Gender column with your ComboBox column + if (e.PropertyName is nameof(Person.Gender)) + { + e.Column = new TableViewComboBoxColumn + { + // Keep the same binding so the column still reads/writes the Gender property + Binding = originalColumn.Binding, + Header = originalColumn.Header, + + // Set the source for the options to the Genders collection (so the options for this example will be "male", "female", etc.) + ItemsSource = viewModel.Genders, + IsEditable = false + }; + } + } +} +``` + +
+ +--- + +## FAQs + +#### Q: Does TableView automatically use a ComboBox if I use a collection? +A: **No.** TableView will not create a ComboBox column automatically just because you have a collection. You must replace the column in the `AutoGeneratingColumn` event and set the `ItemsSource`. + +#### Q: What is a Binding and why do I use it? +A: `Binding` tells the column which property of the row item it should display and edit (for example, `Person.Gender`). `ItemsSource` (for ComboBox columns) is the list of options the user can pick. When replacing a generated column, copy the original column's `Binding` to keep the row-level connection: +```csharp +Binding = originalColumn.Binding +``` + +Have another question? Please create a post in [**Discussions**](https://github.com/w-ahmad/WinUI.TableView/discussions) + +--- + +## Tips +- Always copy the original column's `Binding` and `Header` when replacing columns. +- For custom columns not tied to a model property, add them directly to the TableView.Columns collection. +- You can set properties like `IsReadOnly`, `Width`, `CellStyle`, etc., on any column type. + +--- + +By following these steps, you can add or customize any supported column type in TableView, whether you want to use simple auto-generated columns or advanced custom columns. You do not need to use the sample's model or code. Just follow the patterns above with your own data classes and collections. \ No newline at end of file diff --git a/docs/docs/Adding data to your TableView/Data-To-TableView.md b/docs/docs/Adding data to your TableView/Data-To-TableView.md new file mode 100644 index 0000000..1fbf81d --- /dev/null +++ b/docs/docs/Adding data to your TableView/Data-To-TableView.md @@ -0,0 +1,20 @@ +# Adding data to your TableView +There are three different ways to add columns to your TableView, each one with their own use case. Before you get started, you'll need to find out which method you are going to use to add the columns. + +--- + +### 1. Assigning columns from a collection +This approach lets you create a TableView where the columns are **predetermined**. New rows can be added dynamically. Perfect for simple setups where you have a predetermined amount of columns, like a contact list (where there are columns name, phone number, email) where the user can add a new contact on-demand, or a static list like an ingredients list for a recipe. + +[**👉 Go to documentation**](Assigning-Columns-From-A-Collection.md) + +--- + +### 2. Dynamically creating columns using `DataTemplate`. +More advanced option that allows you to add **new** columns dynamically, while having the same functionality as option 2. Good choice for things like custom lists, where the user can add their own fields and columns. + +[**👉 Go to documentation**](Dynamically-Creating-Columns-Using-Template.md) + +--- + +##### Need help? [**Go to Discussions**](https://github.com/w-ahmad/WinUI.TableView/discussions) \ No newline at end of file diff --git a/docs/docs/Adding data to your TableView/Dynamically-Creating-Columns-Using-Template.md b/docs/docs/Adding data to your TableView/Dynamically-Creating-Columns-Using-Template.md new file mode 100644 index 0000000..c3d1ff1 --- /dev/null +++ b/docs/docs/Adding data to your TableView/Dynamically-Creating-Columns-Using-Template.md @@ -0,0 +1,243 @@ +> [🔙 **Back to *Adding data to your TableView***](Data-To-TableView.md) + +# Dynamically creating columns using a DataTemplate + +This is a more advanced method for adding data to your TableView, but it allows you to **create custom columns and assign data to them on-the-fly**. +It’s your go-to approach for building features such as task trackers, project dashboards, or any app that needs **customizable columns** . For example, letting users add fields like *Due Date*, *Description*, *Status*, *Difficulty*, etc. + +| | This method lets you... | +| --- | ------------------------------------------- | +| ✅ | Display a TableView with custom data | +| ✅ | Edit and access the TableView’s data | +| ✅ | Create new rows dynamically | +| ✅ | Create new custom columns dynamically | +| ✅ | Assign new data values to those new columns | + +> [!NOTE] +> For more advanced columns like `TableViewComboBoxColumn`, you'll need to set up binding and use a `DataTemplate`. + +## 1. Get your data ready + +We’ll start by defining a flexible data model that can store values for any number of columns. +Instead of having fixed properties like `Title`, `Description`, or `Status`, we’ll use a **dictionary-backed indexer** so we can easily add or remove columns without changing the model. + +Here’s what that class looks like: +```csharp +public class DynamicRow : INotifyPropertyChanged +{ + private readonly Dictionary _values = new(); + + public object? this[string key] + { + get => _values.TryGetValue(key, out var v) ? v : null; + set + { + _values[key] = value; + OnPropertyChanged($"Item[{key}]"); + } + } + + public event PropertyChangedEventHandler? PropertyChanged; + private void OnPropertyChanged(string name) + => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); +} +``` + +### How it works + +This class represents **one row** in your `TableView`. +Each row contains a collection of values - one for each column - stored in a dictionary: +- The **key** (the string index like `"Title"` or `"Status"`) represents the column name. +- The **value** is the actual cell value in that column. + +For example, imagine this TableView: +| Title | Description | +| ----------- | ------------------------------------------ | +| Fix bug #43 | App crashes when collapsing NavigationView | +| Review PR | Review the pull request by Georgios1999 | + +Internally, each row is stored as something like this: +```csharp +var row1 = new DynamicRow(); +row1["Title"] = "Fix bug #43"; +row1["Description"] = "App crashes when collapsing NavigationView"; + +var row2 = new DynamicRow(); +row2["Title"] = "Review PR"; +row2["Description"] = "Review the pull request by Georgios1999"; +``` + +Each row is flexible — if you later add a new column (for example, “Status”), you can simply assign: +```csharp +row1["Status"] = "In Progress"; +row2["Status"] = "Pending Review"; +``` + +No need to change your class or rebuild — the rows adapt automatically. + +## 2. Adding dynamic columns + +Once you have your data model (`DynamicRow`), you can add columns dynamically to the TableView in your page’s code-behind. + +Here’s a simple example that adds a **text column** at runtime: +```csharp +private void AddNewColumn() +{ + // These two are optional, but we're using them now to give a unique name to each column + int n = MyTableView.Columns.Count + 1; + string headerText = $"Column {n}"; + + string fieldKey = $"Column{n}"; + + var col = new TableViewTextColumn + { + Header = headerText, // The column's header (its name). We have set it to headerText for this example, but you can have it be anything. + Binding = new Binding + { + Path = new PropertyPath($"[{fieldKey}]"), + Mode = BindingMode.TwoWay + } + }; + + MyTableView.Columns.Add(col); // Adds the new column. + + // Initialize existing rows so they display a default value. This is part of the example and not needed in your code. + foreach (var row in Rows) // for each row... + row[fieldKey] = $"Value {n}"; // set the value +} +``` + +### What’s happening here: +- We create a new **TableViewTextColumn** dynamically. +- The `Binding` path uses `"[ColumnX]"` syntax to connect to our `DynamicRow`’s dictionary. +- The column is added to the TableView, and any existing rows will immediately show their values. + +## 3. About more advanced columns + +For basic columns, such as for text or numbers, the setup we already have is fully sufficient. You don’t need to change the dictionary type. + +The `Dictionary` we use in `DynamicRow` can hold any kind of value: +- text (`string`) +- numbers (`int`, `double`, `decimal`) +- dates (`DateTime`) +- checkboxes (`bool`) +- or even custom objects + +That means this single model supports all standard TableView column types (`TableViewTextColumn`, `TableViewNumberColumn`, `TableViewCheckBoxColumn`, `TableViewDateColumn`, etc.) and it changes automatically based on what type of value you use. + +For example: +```csharp +var row = new DynamicRow(); +row["Title"] = "Fix crash on startup"; // Text column +row["Progress"] = 0.75; // Number column +row["IsComplete"] = true; // Checkbox column +row["DueDate"] = DateTime.Today.AddDays(3); // Date column +``` + +> [!NOTE] +> You should always keep the dictionary key as `string`. +> The key represents the **column name**, and TableView bindings reference it using syntax like `Path=[ColumnName]`. + +## Adding advanced columns like `TableViewComboBoxColumn` +Advanced columns like `TableViewComboBoxColumn` need extra setup. `ComboBox` specifically, has its own items (options), default option and values. We need extra work to set these up. In this example, we're going to set up a `TableViewComboBoxColumn`. + +### Getting everything ready: +#### 1. Get your data ready +You can use the exact same setup from before for this. + +#### 2. Set up a helper function to escape XAML characters +We'll be writing directly into XAML using C# to add the `DataTemplate`. However, the strings need to be interpreted in a special way that doesn't involve using the actual characters. Copy and paste this helper function into your code so you can use it when needed. +```cs +private static string XmlEscape(string s) +{ + if (s is null) return ""; + return s.Replace("&", "&") + .Replace("<", "<") + .Replace(">", ">") + .Replace("\"", """) + .Replace("'", "'"); +} +``` + +#### 3. Declare your class +Declare your data class in your page's class like so: +```cs +... +public sealed partial class MainPage : Page +{ + public ObservableCollection Rows { get; } = new ObservableCollection(); +... +``` +By doing this, you can use it to edit, reference or add columns. + +#### 4. Set your TableView's `ItemsSource` to your data +This sets the items source for your TableView to your rows, so when a new item is added to `Rows`, it also gets added and shown to your TableView. +```cs +public MainPage() +{ + this.InitializeComponent(); + + // Attach our rows as the ItemsSource for the TableView. + MyTableView.ItemsSource = Rows; +} +``` + +### Adding a dynamic ComboBox column +You can also create ComboBox columns dynamically. +This lets you display options like “Easy”, “Medium”, or “Hard” directly in your table and store the selected value for each row. For this example, we're going to create a function called `AddComboBoxColumn()` that adds a new ComboBox column with its own unique names and options +```cs +private void AddComboBoxColumn() +{ + // Give each new column a unique name. + int n = MyTableView.Columns.Count + 1; + string fieldKey = $"Column{n}"; + string headerText = $"Column {n}"; + + // Define the dropdown options for this ComboBox column. + var options = new List { "Easy", "Medium", "Hard" }; + + // Build ComboBox items as XAML text. + var sb = new StringBuilder(); + foreach (var opt in options) + sb.Append($""); + + // Build a DataTemplate that defines the ComboBox for this column. + string xaml = + "" + + $"" + + sb.ToString() + + ""; + + // Convert the XAML string into a real DataTemplate. + var template = (DataTemplate)XamlReader.Load(xaml); + + // Create the TableView column and assign the template. + var col = new TableViewTemplateColumn + { + Header = headerText, + CellTemplate = template + }; + + // Add the new column to the TableView. + MyTableView.Columns.Add(col); + + // Optional: initialize existing rows with a default option. + var first = options.FirstOrDefault(); + if (first != null) + { + foreach (var row in Rows) + row[fieldKey] ??= first; + } +} +``` + +### How it works +- Each column is created dynamically and bound to a unique key in your `DynamicRow` dictionary. +- The list `options` defines the items available in the ComboBox for that column. +- The ComboBox is created in XAML using a `DataTemplate`, and its `SelectedValue` is two-way bound to the row data. +- When you add the column, every row immediately gains that new ComboBox cell — its value is stored per-row in the `DynamicRow` dictionary. +- Optionally, you can initialize rows with a default value (like `"Easy"`) to keep cells from appearing empty. + +## 🎉 And that's it! +This is how you can add your own custom columns dynamically and on-demand in code-behind. If you have any questions, feel free to create a post in [**Discussions**](https://github.com/w-ahmad/WinUI.TableView/discussions). You may tag @Georgios1999 for feedback related to the docs. \ No newline at end of file diff --git a/docs/docs/AutoGenerateColumns.md b/docs/docs/AutoGenerateColumns.md new file mode 100644 index 0000000..fc7a48b --- /dev/null +++ b/docs/docs/AutoGenerateColumns.md @@ -0,0 +1,41 @@ +# About `AutoGenerateColumns` +`AutoGenerateColumns` is used when you want each item of the TableView to be automatically generated. When it is set to `true`, TableView will look at your values and assign them a column depending on their type. Some examples include: +- **`TableViewTextColumn`** for `string` +- **`TableViewNumberColumn`** for `int`, `float`, or `double` +- **`TableViewDateColumn`** and `TableViewTimeColumn` for `DateOnly` and `TimeOnly` repsectively +- **`TableViewCheckBoxColumn`** for `bool` + +Other columns like `TableViewComboBoxColumn` or `TableViewTemplateColumn` need to be handled in code-behind and are not automatically generated. + +--- + +#### `AutoGenerateColumns` also has a related function. +**`OnAutoGeneratingColumns`** is called when the TableView is generating the columns. Inside it you can write code that executes when the TableView is loading the columns. This is great for things like adding `ComboBoxColumns` by checking if the current column that's being loaded should be a `ComboBoxColumn` like so: + +```csharp +// Assign this method to the TableView.AutoGeneratingColumn event +void OnAutoGeneratingColumns(object sender, TableViewAutoGeneratingColumnEventArgs e) +{ + if (sender is not TableView tableView) return; + + // Try to get your view model from the TableView's DataContext + var viewModel = tableView.DataContext as MainViewModel; // replace MainViewModel with your VM type + + // The column that TableView would have used for this property + if (e.Column is not TableViewBoundColumn originalColumn) return; + + // Gander is a ComboBoxColumn + if (e.PropertyName == nameof(Person.Gender) && viewModel != null) + { + e.Column = new TableViewComboBoxColumn + { + // Keep the same binding so the column still reads/writes the Gender property + Binding = originalColumn.Binding, + Header = originalColumn.Header, + + // Set the source for the options to the Genders collection (so the options for this example will be "male", "female", etc.) + ItemsSource = viewModel.Genders + }; + } +} +``` \ No newline at end of file diff --git a/docs/docs/Getting data from your TableView/Reading-Data-From-Your-TableView.md b/docs/docs/Getting data from your TableView/Reading-Data-From-Your-TableView.md new file mode 100644 index 0000000..9b8216e --- /dev/null +++ b/docs/docs/Getting data from your TableView/Reading-Data-From-Your-TableView.md @@ -0,0 +1,86 @@ +# Reading data from your TableView +There are many built-in functions to get data from a TableView. Each one has its own use and output. TableView data is often assigned to a `string`, separated by a character of your choice to make it readable and easy to extract and use later. This doc will go through all the methods that can be used to read data from a TableView. + +## 1. Using `GetAllContent()` +`GetAllContent()` returns a `string` with all the TableView's content (rows, columns, optionally headers) separated with a specified character. +```cs +string data = TV.GetAllContent(true, ','); +``` +It takes 2 arguments: +1. A `bool` that specifies if the column's *headers* (names) should be added as part of the extracted data. +2. A `char` (single character) that will be the character that will separate each cell. + +Therefore if we have a TableView like this: +| Title | Description | +| ----------- | ------------------------------------------ | +| Fix bug #43 | App crashes when collapsing NavigationView | +| Review PR | Review the pull request by Georgios1999 | + +The output would be: +``` +Title,Description +Fix bug #43,App crashes when collapsing NavigationView +Review PR,Review the pull request by Georgios1999 +``` + +## 2. Using `GetRowsContent()` +`GetRowsContent()` returns a `string` with all the content of the specified row(s) separated with a specified character. +```cs +string data = TV.GetRowsContent([0], false, ','); +``` +It takes 3 arguments: +1. An `int[]` (array of integer numbers (so it can be more than one)) that specified which rows to read. For example `[0, 1]` will read the first two rows. +2. A `bool` that specifies if the column's *headers* (names) should be added as part of the extracted data. +3. A `char` (single character) that will be the character that will separate each cell. + +Therefore if we have a TableView like this: +| Title | Description | +| ----------- | ------------------------------------------ | +| Fix bug #43 | App crashes when collapsing NavigationView | +| Review PR | Review the pull request by Georgios1999 | + +The output would be: +``` +Fix bug #43,App crashes when collapsing NavigationView +``` + +## 3. Using `GetCellsContent()` +`GetCellsContent()` returns a `string` with the content of the specified cell(s) separated with a specified character. +```cs +IEnumerable slot = new[] { new TableViewCellSlot { Row = 0, Column = 1 } }; +string data = TV.GetCellsContent(slot, false, ','); +``` +It takes 3 arguments: +1. An `TableViewCellSlot`, which defines the slot of the column to read from. +2. A `bool` that specifies if the column's *headers* (names) should be added as part of the extracted data. +3. A `char` (single character) that will be the character that will separate each cell. + +Therefore if we have a TableView like this: +| Title | Description | +| ----------- | ------------------------------------------ | +| Fix bug #43 | App crashes when collapsing NavigationView | +| Review PR | Review the pull request by Georgios1999 | + +The output would be: +``` +App crashes when collapsing NavigationView +``` + +## 4. Using `GetSelectedContent()` +`GetSelectedContent()` returns a `string` with the content of the items the user has selected with their cursor. +```cs +string data = TV.GetSelectedContent(true, ',') +``` +It takes 3 arguments: +1. A `bool` that specifies if the column's *headers* (names) should be added as part of the extracted data. +2. A `char` (single character) that will be the character that will separate each cell. + +## Honorable Mentions +### Extracting to a CSV +TableView can extract its contents to a CSV file to view in programs like Excel. This can be done using the corner button. See it in action in the [Samples app](https://github.com/w-ahmad/WinUI.TableView.SampleApp). + +### Extracting from your set data (Not recommended) +**This approach is not recommended since it is unnecessarily complicated and might not work well for dynamic columns.** If you have set your columns up using code-behind like the approach mentioned [here](../Adding%20data%20to%20your%20TableView/Assigning-Columns-From-A-Collection.md). We're going to use the `People` example from there: +```cs +string data = People[0].Name.ToString() + "," + People[0].Age.ToString() + "," + People[0].IsActive.ToString(); +``` \ No newline at end of file diff --git a/docs/docs/customization.md b/docs/docs/customization.md index a1c9c0f..c282ed4 100644 --- a/docs/docs/customization.md +++ b/docs/docs/customization.md @@ -33,4 +33,21 @@ You can customize the appearance and behavior of the `TableView` by modifying it
-``` \ No newline at end of file +``` + +#### 👉 Use the [Sample App](https://github.com/w-ahmad/WinUI.TableView.SampleApp.git) to easily try out and change customization options. + +## Editing properties in C# +To customize your TableView using C#, first give it a name: +```xml + +``` +then you can edit its properties in code like this: +```csharp +TView.Opacity = 1; +``` + +### Useful Properties and Functions +- `TableView.Columns` allows you to edit columns by using `.Add()` to add a column, `.Remove()` to remove one, `.Clear()` to clear all and more +- `TableView.ScrollIntoView(`any column item`)` Scrolls to the chosen item +- `TableView.SelectAll()` Selects all the items \ No newline at end of file diff --git a/docs/docs/getting-started.md b/docs/docs/getting-started.md index d0c3916..9962670 100644 --- a/docs/docs/getting-started.md +++ b/docs/docs/getting-started.md @@ -14,6 +14,9 @@ Install-Package WinUI.TableView ``` ### 3. Add `WinUI.TableView` to Your XAML +> [!TIP] +> Make sure to read the [docs](https://github.com/w-ahmad/WinUI.TableView/tree/main/docs) to learn more about TableView. + In your `MainWindow.xaml`, add the `WinUI.TableView` control: ```xml @@ -127,5 +130,4 @@ public sealed partial class MainWindow : Window Build and run your application. You should see the `WinUI.TableView` populated with the rows and cells from your `Items` collection. Here is the result by running the app on Desktop platform. -![image](https://github.com/user-attachments/assets/e00bffc7-19e0-40bd-bbda-07198d6bc60a) - +![image](https://github.com/user-attachments/assets/e00bffc7-19e0-40bd-bbda-07198d6bc60a) \ No newline at end of file diff --git a/docs/docs/toc.yml b/docs/docs/toc.yml index c18a8d2..664609a 100644 --- a/docs/docs/toc.yml +++ b/docs/docs/toc.yml @@ -5,4 +5,4 @@ - name: Getting Started with Uno href: getting-started-with-uno.md - name: Customization - href: customization.md + href: customization.md \ No newline at end of file