How would I make an XAML layout which had bound properties and events? I'm going out of my mind.
First of all, it's for a chat application and a single room chat works fine, in the sense that I can design the layout of the application in XAML with name properties here there and everywhere in order to control it in C# but when it comes to repeating this layout multiple times because of multiple rooms, it becomes a bit of a problem. I was doing it solely by C# this lead to hundreds of lines of just defining controls and adding them to the window, and one problem with that would be the fact that name properties would collide.
I was going to go with modifying a ControlTemplate of a random control for example a Frame, but then I run into the issue of defining custom properties and events.
I just have no idea how I can do what I want to do. I've asked for help in many places to no avail.
I am honestly going out of my mind, and on the verge of giving up entirely.
My aim is to have a tabcontrol with multiple rooms, I need to be able to access controls in each room with ease so I can modify the content. I'm just getting no where.
Edit
Public chat template is obviously different to the private chat template, hence why I've failed so badly at this.
You can do it using MVVM pattern which is preferable when dealing with WPF.
However, this requires some experience and a lot of mind-warping.
Luckily, you can always use classic approach if you are coming from the Windows Forms world.
Just create an user control for the chat room which contains GUI, data, logic, event handlers, ...
Place instances of this chat room user control inside tab container and you are done.
Sounds like a perfect place for a data template!
First off, you need a "ChatRoom" class that contains all the state information for a given room. Then your main ViewModel needs to have a collection of these objects. Finally, set up your tab control with a DataTemplate that is probably nearly identical to your current window.
The TabControl would look like:
<TabControl ItemsSource="{Binding ActiveRooms}">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
... (All your chat room stuff)
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
That way, whenever you create a new room (ActiveRooms should be an ObservableCollection, by the way) it automatically a new set of controls and binds them to the new room's instance properties.
Related
I need to use a different panel for a particular section/group in my ListView. How do I do that (using XAML, C#, or anything)? I already tried using GroupedStyleSelector but it didn't work (I researched about it but it turned out it's not designed for this purpose). Here's my XAML right now:
<ListView ItemsSource="{Binding Source={StaticResource cvs}}">
<ListView.GroupStyle>
<GroupStyle HidesIfEmpty="True">
<GroupStyle.Panel>
<ItemsPanelTemplate>
// I want to change this for a particular group
<uwp:SGStaggeredPanel/>
</ItemsPanelTemplate>
</GroupStyle.Panel>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
I'm thinking of subclassing the panel, but the problem is how do I get a reference to the current group?
https://learn.microsoft.com/en-us/windows/communitytoolkit/extensions/listviewbase
The above article talks about a WCT goody that allows you to dynamically change the Tamplate of the item that is about to be rendered, this particular example is a statically expressed extension that simply works as an attached property to a listview and cycles through two different templates
But you can easily extend ListView into a templated control and then more easily have access to the Viewmodel that houses your Itemsource, from then you can go on to change the
private static void ItemTemplateContainerContentChanging(Windows.UI.Xaml.Controls.ListViewBase sender, ContainerContentChangingEventArgs args)
which is where all the magic takes place.
Notation for implementation
Note 0:
if you don't know mvvm and binding, forget you ever read this and go study it up instead.
Note 1:
All child controls that have no explicitly defined Data Context will inherit their parents.
Note 2:
You will be able to Map incoming controls in the aforementioned function by tracking the incoming args.ItemIndex and then cross checking it with the binded source (Observable list etc) that is housed on the underlying datacontext.
Note 3:
To convert this into a tamplated/custom control you will have to pretty much make your own implementation of ListView like this MyListview:ListView
The Dependency properties will have to be converted to conventional ones,
just type 'propdp' and double tap Tab, to bring up the default tamplate.
You will still have to reference all the different DataTamplates from XAML like its shown in the showcase app listed bellow.
Note 4:
Cut the slack off that showcase code, the stretch direction and the zebra stripes for example are not needed in your case.
https://github.com/windows-toolkit/WindowsCommunityToolkit/blob/master/Microsoft.Toolkit.Uwp.UI/Extensions/ListViewBase/ListViewExtensions.cs
this is the exact location of the code piece i talked about, to check it out in action and play with it, Download 'Windows Community Toolkit' from the store, it is in the Extensions section.
Im building an application where I want to head for a design, that could remind of a dockpanel.
What I want, is having buttons in the left side (or left panel) representing different areas of the application (e.g "Milk", "Bread") and then have different "views" in the middle-panel.
What I already have tried, is making an application with a "Frontpage", and buttons changing the whole window/usercontrol - this however will not give me static areas/panels.
I do not want to use a tabcontrol with the tabtitemstrip being vertical - however it is kinda the same functionality im looking to have.
Any ideas?
Below is a picture with the wished design, to kinda give an idea of my thoughts.. Any help appreciated :)
http://s57.photobucket.com/user/RolleKn/media/wpfdesign_zps3737b014.jpg.html
If you use WPF, use ContainerControl or ContentPresenter for that.
In general, "switching Visibility On/Off" is not a good way to go. It forces the UI to create all objects, even those invisible ones, and to handle their data and events, etc.
And you need to switch it all manually.
WPF provides you with many mechanisms that can save you this. Some are smarter than others, some not.
One of the most basic mechanism in WPF is the Control and its Template property. You can replace whole your Grid+Contents+SwitchingVisibility idea with a single Control and switching its Template:
<Window.Resources>
<ControlTemplate x:Key="panel1"> ..carrots.. </ControlTemplate>
<ControlTemplate x:Key="panel2"> ..cucubers.. </ControlTemplate>
<ControlTemplate x:Key="panel3"> ..donkey.. </ControlTemplate>
...
</Window.Resources>
<Grid x:Name="LayoutRoot">
<Control x:Name="foo" />
</Grid>
Now, if you get the foo and set its .Template and set it to panel1, then the "carrots" will show up. if you set it to panel3, donkeys. And so on.
It's very powerful, but it will not be really handy due to some other things I won't cover. There are books and tutorials that explain Templates in depth. Also, this mechanism is really not designed for such task. It's the most basic one, and a good thing to know if you want to work in WPF, but there are more suitable ones here.
Second next powerful and still basic mechanism is ContentControl/ContentPresenter. They work almost in the same way (actually CC uses CP internally), so I'll skip it.
ContentControl is a smart control that knows how to automatically select a correct Template with respect to the data you are tryng to present.
So:
<Window.Resources>
<DataTemplate DataType="CarrotData"> ..carrots.. </..>
<DataTemplate DataType="CucumberData"> ..cucubers.. </..>
<DataTemplate DataType="DonkeyData"> ..donkey.. </..>
...
</Window.Resources>
<Grid x:Name="LayoutRoot">
<ContentControl x:Name="foo" Content="{Binding ..}" />
</Grid>
Note the change from 'ControlTemplate' to 'DataTemplate'.
Now, with this setting, you don't even need to switch templates manually. You just get the "foo" and set its Content to either:
a CarrotData object, that contains the carrot-related data
a CucumberData object, that contains the cucumber-related data
a DonkeyData object, that contains the donkey-related data
Once you set the data to be shown (i.e. foo.Content = carrots[5]), the ContentControl will pick the relevant template to be shown.
You can bind the Content property to just about anything. If you have some dataclass that contains carrots/donkeys and has a property CurrentThing, you can bind to it and ContentControll will switch the views automatically along with the changes to CurrentThing.
That's basics. There's much more to it, in almost any point I tried to briefly cover. For now, leave ControlTemplates. Read about DataTemplates and Bindings. Read about ContentPresenter (shows 1 template for 1 item) and ItemsControl (shows N items+templates). Then, read a little on MVVM pattern.
You will quickly see that "having everything in one Grid" and "switching Visibility" is an odd way to do it.
However, I wouldn't be fair if I didn't mention that everything has a cost included. Extensive use of templates and bindings makes your app a bit slower compared to what you could get when you do everything manually. But usually, doing it manually is just not really worth it.
I have a treeview at the left side of the screen, and when I click on any of the TreeViewItem, I want the right side of the screen to change accordingly.
For example, clicking on 'Project' would display on the right half of the screen, a label for project name along with the project name in a text box, and a similar label-textbox pair for some other fields. Clicking on a sub-option of 'Project' such as 'Task 1' should change the right half of the screen such that instead of labels and textboxes for project name and details, it should now be for task name/details. Atm, I only care about label-textbox pairs but in the future I'll need some more sophisticated options, maybe buttons and tables.
What I thought of was to have a grid premade for each option, when I clicked on 'Project' there would be a grid which displays all the info for a Project. And when I then clicked on 'Task 1', the Project grid should be hidden and the Task grid should be displayed with the fields filled out.
Is this possible? What should I be using to create templates that I can then choose from?
Firoz already mentioned the important bit. A rough guess is that you're not using MVVM pattern, so to minimize the adaption effort, you could add a Content Control to your window and set the content of this control whenever a selection is made. You can put any User Control in there.
Using MVVM would mean you bind that Content Control to a property on your ViewModel (of type UIElement or UserControl) and set an instance whenever a bound selected values changes. Speaking of selected Value, I think the default TreeView is not really Binding-friendly, so you might end up with behaviours that do the binding for you.
What you are asking to do is quite easy and possible, but I don't think you are thinking quite big enough.
As your project grows and the number of different things that you want to show expands, then you are going to need to show and hide more and more controls. This is quite quickly going to get unmanageable. Instead think about some other controls deal with this, in some ways you are doing something very like a tabbed dialog, just with a hierarchical set of tabs.
A tabbed dialog has a panel and a set of tabs, when you click on each tab, the content of the panel changes. In fact you can create UserControls one for each specialised set of UI that you want to display, e.g. you could have a ProjectControl that displays all of your project textboxes, labels, buttons etc.
In addition WPF has this neat feature called DataTemplates, these define how a type of data should look when it is displayed. So if you where to have a
public class MyProject
{
public string Name {get;set;}
}
Then you could define
<DataTemplate DataType="{x:Type MyProject}>
<TextBox Text="{Binding Name}"/>
</DataTemplate>
And WPF will automatically convert the data into to its visual form if you set it as the content of the tab panel.
However this type of displaying content in a panel is not the only WPF control that does this. There is also something called a NavigationFrame, which also can be used wrapped into a Window as a NavigationWindow. This control provides you ways to navigate to the next Page to display. Pages can be just like the UserControls in a tabbed dialog, but can also be URIs, enabling you to link in content from the web if you wish. In addition you can call NavigateTo from other controls enabling you build much more usable interfaces.
I worked through the process of building a full windows control panel style interface in
http://alski.net/post/2012/01/11/WPF-Wizards.aspx
and http://alski.net/post/2012/01/13/WPF-Wizards-part-2-Glass.aspx
I've added later VS2012 style glows in
http://alski.net/post/2013/09/14/WPF-Re-creating-VS2012Office-2013-window-glow.aspx
And then released the entire source code as open source at
http://winchrome.codeplex.com/
This comes with support for embedding Navigation panels with
<WinChrome:SearchableNavigationWindow
x:Class="WinChrome.Win7Demo.MainWindow"
...
xmlns:WinChrome="clr-namespace:WinChrome;assembly=WinChrome"
Style="{StaticResource Win7NavigationWindow}">
<WinChrome:SearchableNavigationWindow.Navigation>
<view:Navigation x:Name="navigationTree"/>
</WinChrome:SearchableNavigationWindow.Navigation>
(Full source code)
Where the navigation window is embedded as, but can also be a TreeView.
<UserControl x:Class="WinChrome.View.Navigation" ...>
<ScrollViewer HorizontalScrollBarVisibility="Disabled" Padding="12,0"
VerticalScrollBarVisibility="Auto" >
<StackPanel>
<Button
Margin="0,12,0,0" Style="{StaticResource LinkNavigatorButtonStyle}"
Content="Home"
Command="{Binding
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Win7Demo:MainWindow}, AncestorLevel=1},
Path=GoHomeCommand}" />
</StackPanel>
</ScrollViewer>
(Full source code)
I'm working on making my first game using C# and XAML for Windows 8. I'm still learning the core concepts and best practices, and MVVM has been a hurdle. I'll attempt to ask the question in two parts.
Background
The game I'm making is Sudoku. Sudoku has a board that contains a 9x9 grid of tiles. I have three models - Game, Board, and Tile. When a Game is created, it automatically creates a Board, and when the Board is created, it creates 81 (9x9) Tiles.
1. With a hierarchy of views, how are corresponding view models created?
To match the hierarchy of models, I would like to have a hierarchy of views (GameView contains a BoardView which contains 81 TileViews). In XAML, it's pretty easy to create this hierarchy of views with user controls, but I don't understand how the view models get created.
In the examples I've seen, the data context of a user control is often set to the view model (using the ViewModelLocator as a source) which creates a fresh instance of the view model. This seems to work well if you have a flat view, but also seems like it gets messy when you have a hierarchy. Does the GameView create a GameViewModel and leave it up to its BoardView child to create a BoardViewModel? If so, how does the GameViewModel communicate with the BoardViewModel? Can the BoardViewModel communicate back up the hierarchy to the GameViewModel?
2. How does a view model get model data?
In iOS, I would start by using a service to fetch a Game model that was pre-populated with data. I would then create a GameViewController view controller (which was in charge of creating the view) and pass the Game to it. In MVVM, I see the value in having a view be in charge of creating its own view model (ideally using a ViewModelLocator), but I don't understand how that view model gets the model.
In all of the examples I've found online, the view model uses some service to fetch its own data. But I haven't come across any example that accepts constructor params or params passed from a higher level of navigation. How is this done?
I don't want to use an application resource or some other kind of singleton storage method for my model because, not that I do, but what if I wanted to display multiple puzzles on the screen at once? Each GameView should contain its own Game.
Not only does the GameViewModel need a reference to the Game model, but the BoardViewModel that was created somehow (see question 1) needs a reference to the Board model that belongs to the Game model. The same goes for all the Tiles. How is all this information passed down the chain? Can I do this much heavy lifting entirely within XAML, or am I going to have to do some sort of binding or other initialization in code?
Phew!
I appreciate any advice you can give, even if it's not a full answer. I'm also keen to find any examples of MVVM projects that share similar challenges to my own. Thanks a ton!
I would start by creating a class to begin the application with. Typically I call that class something like ApplicationViewModel or ShellViewModel, even though technically it can abide by different rules than what I would typically use for a ViewModel
This class gets instantiated at startup, and is the DataContext for the ShellView or ApplicationView
// App.xaml.cs
private void OnStartup(object sender, StartupEventArgs e)
{
var shellVM = new ShellViewModel();
var shellView = new ShellView();
shellView.DataContext = shellVM;
shellView.Show();
}
This is usually the only place I set a DataContext for a UI component directly. From this point on, your ViewModels are the application. Its important to keep this in mind when working with MVVM. Your Views are simply a user friendly interface that allows users to interact with the ViewModels. They're not actually considered part of the application code.
For example, your ShellViewModel may contain:
BoardViewModel CurrentBoard
UserViewModel CurrentUser
ICommand NewGameCommand
ICommand ExitCommand
and your ShellView might contain something like this:
<DockPanel>
<Button Command="{Binding NewGameCommand}"
Content="New Game" DockPanel.Dock="Top" />
<ContentControl Content="{Binding CurrentBoard}" />
</DockPanel>
This will actually render your BoardViewModel object into the UI as the ContentControl.Content. To specify how to draw your BoardViewModel, you can either specify a DataTemplate in ContentControl.ContentTemplate, or use implicit DataTemplates.
An implicit DataTemplate is simply a DataTemplate for a class that doesn't have an x:Key associated with it. WPF will use this template anytime it encounters an object of the specified class in the UI.
So using
<Window.Resources>
<DataTemplate DataType="{x:Type local:BoardViewModel}">
<local:BoardView />
</DataTemplate>
</Window.Resources>
will mean that instead of drawing
<ContentControl>
BoardViewModel
</ContentControl>
it will draw
<ContentControl>
<local:BoardView />
</ContentControl>
Now the BoardView could contain something like
<ItemsControl ItemsSource="{Binding Squares}">
<ItemsControl.ItemTemplate>
<ItemsPanelTemplate>
<UniformGrid Rows="3" Columns="3" />
</ItemsPanelTemplate>
<ItemsControl.ItemTemplate>
</ItemsControl>
and it would draw a board using a 3x3 UniformGrid, with each cell containing the contents of your Squares array. If your BoardViewModel.Squares property happened to be an array of TileModel objects, then each grid cell would contain a TileModel, and you could again use an implicit DataTemplate to tell WPF how to draw each TileModel
Now as for how your ViewModel gets its actual data objects, that's up to you. I prefer to abstract all data access behind a class such as a Repository, and have my ViewModel simply call something like SodokuRepository.GetSavedGame(gameId);. It makes the application easy to test and maintain.
However you get your data, keep in mind that the ViewModel and Models are your application, so they should be responsible for getting data. Don't do that in the View. Personally I like keeping my Model layer for plain objects that hold data only, so only ever perform data access operations from my ViewModels.
For communication between ViewModels, I actually have an article on my blog about that. To summarize, use a messaging system such as Microsoft Prism's EventAggregator or MVVM Light's Messenger. They work like a kind of paging system: any class can subscribe to receive messages of a specific type, and any class can broadcast messages.
For example, your ShellViewModel might subscribe to receive ExitProgram messages and close the application when it hears one, and you can broadcast an ExitProgram message from anywhere in your application.
I suppose another method would be to just attach handlers from one class to another, such as calling CurrentBoardViewModel.ExitCommand += Exit; from the ShellViewModel, but I find that messy and prefer using a messaging system.
Anyways, I hope that answers some of your questions and will point you in the right direction. Goodluck with your project :)
I am trying to learn the MVVM pattern (C#), having come from a Windows Forms background. I am using the MVVM Light toolkit, and so far I think it is brilliant.
I have made several small applications, however one thing I am struggling with is introducing a second view.
I want to (for example), have a button on my MainViewModel, which via a RelayCommand, opens up a new Window - let's say an "About" window. I have done hours of research on the web for this however it seems I can't get my AboutViewModel to communicate with/show my AboutView.
I have placed a receiving messenger in the code-behind constructor of the AboutView.xaml - however I can't get it to receive any messages from the AboutViewModel, and thus can't make it 'Show()'.
If anyone has an example of an Mvvm Light WPF app using multiple views that would be great :)
There are two ways I can think to do this easily
The first would be to use a Popup instead of a new Window. For example, I often put properties in my ViewModel for PopupContent and IsPopupVisible, and set those values anytime I want to display my Popup control. For example, a ShowAboutPopup relay command might run something like this:
void ShowAboutPopup()
{
PopupContent = new AboutViewModel();
IsPopupVisible = true;
}
You can display it using a Popup object, or a custom UserControl. I prefer to use my own custom Popup UserControl, which will usually end up looking like this:
<Window>
<Canvas x:Name="RootPanel">
<SomePanel>
<!-- Regular content goes here -->
</SomePanel>
<local:PopupPanel Content="{Binding PopupContent}"
local:PopupPanel.IsPopupVisible="{Binding IsPopupVisible}"
local:PopupPanel.PopupParent="{Binding ElementName=RootPanel}" />
</Canvas>
</Window>
The PopupContent property is a ViewModel (such as an AboutViewModel), and DataTemplates are used to tell WPF to draw specific ViewModels with specific Views
<Window.Resources>
<DataTemplate DataType="{x:Type local:AboutViewModel}">
<local:AboutView />
</DataTemplate>
</Window.Resources>
The other method is to have some kind of ApplicationViewModel that runs on startup, and is responsible for the overall application state, which includes which window(s) are open.
Typically I prefer to have a single ApplicationView that contains a ContentControl to display the current page
<Window>
<ContentControl Content="{Binding CurrentViewModel}" />
</Window>
however it can also be used to manage multiple windows. If you do use it to manage multiple Window objects, be warned that this will not be a pure ViewModel because it will need to access some View-specific objects, and referencing UI objects it not something a ViewModel should do. For example, it may subscribe to receive ShowWindow messages, and upon receiving those messages it would create the specified View and show it, and possibly hide the current window as well.
Personally, I try to avoid multiple windows as much as possible. My usual method is to have a single View that contains consistent application objects for any page, and a ContentControl containing dynamic content that changes. I have an example using this navigation style on my blog if you're interested
As i can see you want a navigation in your MVVM app?
Word goes to the creator of MVVM Light - Laurent Bugnion - with his post about using Navigation Service for switching Views. It's actually about Windows Phone & Silverlight but same should apply to WPF.
Also this answer in related question uses this approach.