|
Could you send me what you're trying to do and I'll see if I can get it to work? I've had some experience hacking around in the App class.
|
|
|
|
|
Hey All,
I'm just looking a spot of MVVM and I've written a little demo app with this interface in:
public interface IView<T> where T : ViewModelBase
{
T Model { set; }
}
So in my windows I can do this:
public partial class MainWindow : IView<MainWindowViewModel>
{
public MainWindow()
{
InitializeComponent();
}
public MainWindowViewModel Model
{
set
{
DataContext = value;
}
}
}
And it will automatically type my Model property for me ... amazingly lazy but I kinda like it. I'm just not sure how 'MVVM' it is ...
What are your objections? This isn't an interface for every ViewModel type but works well for Window / Region / Views (proper) type view models ...
Cheers,
|
|
|
|
|
It's probably not that useful: it's rare that you want to manipulate a view's view model – what a mouthful that is – in a generic way, through ViewModelBase. But I think it's quite elegant, and it clearly shows (and enforces!) the M-VM relationship.
|
|
|
|
|
Yeah, it really is a 'swings and roundabouts' kinda issue isn't it ...
Strictly speaking the view still doesn't now anything about the model. The only thing it does know in this scenario is the type it will accept as the view model.
99% of the time your unlikely to want to be switching the type of view model you inject into the view data context.
The only thing I don't like is you now only deal with concrete types for view models rather than an interface.
|
|
|
|
|
To be honest mate, if I were you I would use MefedMVVM instead. It gives you the type to interface, and takes care of hooking up the concrete implementation for you.
|
|
|
|
|
Hey Pete,
Will look into this. Looks good!
Cheers,
|
|
|
|
|
It is - I did a minor part of the development for Marlon. It's top notch stuff.
|
|
|
|
|
Nice!
|
|
|
|
|
<slaps forehead=""> ...
I'm still on .NET 3.5 SP1 at work ...
|
|
|
|
|
Personally, I'm more a fan of including a controller in there. I kind of mix MVVM and MVC together.
My class defenitions then look like this:
public class SomeController : Controller<ISomeView, SomeModel>
public class SomeModel : Model
public class SomeView : View<SomeController>, ISomeView
In the constructor of the view, I'll create the controller, which exposes a model property.
That model will then be set on the datacontext. When creating the controller, I'll pass the view to the constructor so that the controller has a reference to the view as well (under the form of an interface).
This way, you can unit test the controller and assert your model states while mocking the view... and, you won't have any ugly code-behind in your *.xaml.cs file, aside from some eventhandler methods like a button_Click. And in those methods, you'll only have 1 line anyway, which will be a controller call like Controller.XButtonClicked() or whatever.
For LOB applications, this works great.
This also opens up a lot of possibilities for standard control behaviour you may wish to have like automatic busy indicators etc.
modified on Thursday, April 28, 2011 8:10 AM
|
|
|
|
|
Hi,
I have a listbox.item template control like this in my application..
<ListBox Grid.Row="1" Grid.Column="2" x:Name="lbDirectiveList" MaxHeight="200" SelectionChanged="lbDirectiveList_SelectionChanged" SelectionMode="Single" ScrollViewer.VerticalScrollBarVisibility="Auto">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="4">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="25" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Path=DirectiveName}" Grid.Column="0" FontWeight="Bold" MinWidth="200"/>
<TextBlock Text=" - " Grid.Column="1"/>
<TextBlock Grid.Column="2" Text="{Binding Path=DirectiveDescription }" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I have to read the data from this listbox and display it in textboxes when user click on the list box, i have used a code like this, it is displaying the data in UI control, but when am clicked on delete by selecting the row from listbox, it is giving exception, so could u tell me the right way of doing it, it is needed for many validation in my application, i tried for some ex, but dint get a clear idea of how to do it, so any help on this...
private void lbDirectiveList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
gDirectiveDetails.IsEnabled = true;
int i = lbDirectiveList.SelectedIndex;
if (i < listdata.Count && i >= 0)
btnDeleteDirective.IsEnabled = true;
Directive lstdata = (Directive)lbDirectiveList.SelectedItem;
txtDirectiveName.Text = lstdata.DirectiveName.ToString();
txtDirectiveDescription.Text = lstdata.DirectiveDescription.ToString();
}
private void BtnDeleteDirectiveClicked(object sender, RoutedEventArgs e)
{
int i = lbDirectiveList.SelectedIndex;
if (i < listdata.Count && i >= 0)
{
listdata.RemoveAt(i);
}
btnDeleteDirective.IsEnabled = false;
}
|
|
|
|
|
Can you tell what is the exception?
Anyway try this :
listdata.Remove((Directive)lbDirectiveList.SelectedItem);
I would like to suggest you to make few more changes.
You can remove the SelectionChanged event if you want to display the textboxes on selection. You should use the Binding concept of WPF. So you can use IsSynchronizedWithCurrentItem property of the ListBox . I will explain later its use.
This is how you can use it :
1) Set the DataContext of the panel(in your case its grid) to listdata, and remove the ItemsSource property that you have set earlier. So replace it with this :
myGrid.DataContext = listdata;
2) In the XAML of the ListBox, add this :
<ListBox Grid.Row="1" ItemSource = "{Binding}" IsSynchronizedWithCurrentItem = "True" Grid.Column="2" ..../>
3) In the textboxes of your XAML, add this :
<TextBox Name = "txtDirectiveDescription" Text = "{Binding Path = DirectiveDescription}"/>
<TextBox Name = "txtDirectiveName" Text = "{Binding Path = DirectiveName}"/>
4) Remove the SelectionChanged event, you won't be needing it!
With IsSynchronizedWithCurrentItem property, the current item of the DataContext selected on the ListBox will be set to the children of the Panel with which it has Binding. You will better understand by if you set this property to false.
Good luck!
People with high attitude deserve the standing ovation of our highest finger!
My Blog![ ^]
|
|
|
|
|
No my previous code is fine...
it is giving problem only when i used to display the data in a textboxes which is selected from listbox,
it will display data in the particular UI COntrol, but if i clicked on delete here by selecting a listbox data, it is giving exception,
Exception is Null Reference is Unhandled(Object reference not set to an instance of an object).
i dont know why it is giving exception like null refence when clicked on delete...
So any help in fixing this...
when it come back to btndeletedirective, it is taking the value of i as -1(i=-1) and its reading the i value as 0 in textchanged event if i select the data which is in 3rd row in a listbox..
private void txtDirectiveName_TextChanged(object sender, TextChangedEventArgs e)
{
string strDirective = txtDirectiveName.Text.Trim();
string strDescription = txtDirectiveDescription.Text.Trim();
int i = lbDirectiveList.SelectedIndex;
if (!string.IsNullOrEmpty(strDirective))
{
listdata.Insert(i, new Directive { DirectiveName = strDirective, DirectiveDescription = strDescription });
lbDirectiveList.SelectedIndex = i;
listdata.RemoveAt(i + 1);
}
}
private void txtDirectiveDescription_TextChanged(object sender, TextChangedEventArgs e)
{
string strDirective = txtDirectiveName.Text.Trim();
string strDescription = txtDirectiveDescription.Text.Trim();
int i = lbDirectiveList.SelectedIndex;
if (!string.IsNullOrEmpty(strDescription))
{
listdata.Insert(i, new Directive { DirectiveName = strDirective, DirectiveDescription = strDescription });
lbDirectiveList.SelectedIndex = i;
listdata.RemoveAt(i + 1);
}
}
private void lbDirectiveList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
gDirectiveDetails.IsEnabled = true;
int i = lbDirectiveList.SelectedIndex;
if (i < listdata.Count && i >= 0)
btnDeleteDirective.IsEnabled = true;
Directive lstdata = (Directive)lbDirectiveList.SelectedItem;
txtDirectiveName.Text = lstdata.DirectiveName.ToString();
txtDirectiveDescription.Text = lstdata.DirectiveDescription.ToString();
}
private void BtnDeleteDirectiveClicked(object sender, RoutedEventArgs e)
{
int i = lbDirectiveList.SelectedIndex;
if (i < listdata.Count && i >= 0)
{
listdata.Remove((Directive)lbDirectiveList.SelectedItem);
}
btnDeleteDirective.IsEnabled = false;
ResetControls();
}
modified on Wednesday, April 20, 2011 9:43 AM
|
|
|
|
|
I am not able to understand why are you using the TextChanged events, and why are you inserting and removing items there. You seriously need to make changes in your code. That's really bad way of programming.
I think you don't want to use any button to add items, fine, you can use the LostFocus event of the TextBox so that after writing in the TextBox, when the focus on the TextBox is lost, you can then add items. TextChanged event is not the right way.
This maybe the reason for that exception.
People with high attitude deserve the standing ovation of our highest finger!
My Blog![ ^]
|
|
|
|
|
As Tarun tried to explain, this is really the wrong way to tackle this problem. You're trying to write a WPF application like a WinForms one, and that defeats the purpose entirely.
The key to WPF is Data Binding... The entire interface revolves around it, and if you switch your mind to the right track, it'll make your life much easier. All of that "glue" to connect controls, keep things synchronized, turn things on and off... All of that can be pretty much automatic.
First of all, you should try to AVOID giving names to your controls. If you have to refer to a control from the code-behind, you're either doing something really weird/original, or you're just doing it wrong.
1) Create a data model class... Just a simple class that contains a few properties to store the state of your GUI. In your situation, I think you'll need properties for:
a) A list representing the Directives. I'd type this as an ObservableCollection<Directive> to make sure updates are processed correctly.
b) A Directive representing the selected item ("SelectedDirective" maybe).
2) Set the DataContext of the window (Or anything that contains all of the related controls) to this model class, so the bindings will reference it
3) Bind the controls:
a) The ItemsSource of the listbox should bind to your list property
b) The SelectedItem of the listbox should bind to your SelectedDirective property, with Mode=TwoWay
c) The textboxes (txtDirectiveName, txtDirectiveDescription - Though you should un-name them after you do this) should bind to "SelectedDirective.DirectiveName" and "SelectedDirective.DirectiveDescription"
d) The delete button's "IsEnabled" property should bind to SelectedItem. For this, you need a converter that will take an object and return true if it's non-null.
e) The delete button's event handler can be as simple as Directives.Remove(SelectedDirective) .
See how this works? The code-behind never refers directly to GUI controls, except to set the DataContext. The GUI does its own thing, and just reads and writes data to/from the underlying model.
|
|
|
|
|
That's a good answer. Rocky should use the powerful WPF features here. See his reply to my answer and the way he inserts the item.
People with high attitude deserve the standing ovation of our highest finger!
My Blog![ ^]
|
|
|
|
|
Yeah, I noticed... Definitely WinForms-style.
|
|
|
|
|
Definitely a Good Answer.
Regards - Kunal Chowdhury | Microsoft MVP (Silverlight) | CodeProject MVP | Software Engineer
|
|
|
|
|
Ok, i did this by using Binding method and its working fine...
But could u tell me, how can i read the data from that listbox in my code, because when am passing a soap request i need to read the DirectiveName and Descriptionn data seperately,so how can i do this...
|
|
|
|
|
You don't. Your code should never have to touch the ListBox. The ListBox's SelectedItem property binds to a property in your model, and you read THAT property to get the current value.
|
|
|
|
|
Is it possible to put controls in app resources and use then as elements inside a StackPanel ?
A bit of context: I have a UserControl which I use like this:
<Window ...
xmlns:local="clr-namespace:CRRT" ...>
<local:QueryControl>
<ScrollViewer>
<StackPanel>
<local:FeatureSelectionControl x:Key="GroupOwnerCRDSCode" Table="GroupOwnerCRDSCode" Label="Customer Group" Margin="12,12,18,9.999">
<local:FeatureSelectionControl.SelectStatement>
SELECT blablabla
</local:FeatureSelectionControl.SelectStatement>
</local:FeatureSelectionControl>
<local:FeatureSelectionControl Table="RMPMCode" Label="RMPM Code">
<local:FeatureSelectionControl.SelectStatement>
select rarara
</local:FeatureSelectionControl.SelectStatement>
</local:FeatureSelectionControl>
etc etc etc
Each local:FeatureSelectionControl fetches a list of values from a database and lets the user choose one of more values.
I will need to use the same FeatureSelectionControls (i.e. with the same Table/Label/SelectStatement combination) in a dozen of dialog boxes. Since the SQL may change I don't want to duplicate the elements all over. So I thought I'd put them in an application resource. But first let's try with a window resource:
<Window.Resources>
<local:FeatureSelectionControl x:Key="GroupOwnerCRDSCode" Table="GroupOwnerCRDSCode" Label="Customer Group">
<local:FeatureSelectionControl.SelectStatement>
SELECT blablabla
</local:FeatureSelectionControl.SelectStatement>
</local:FeatureSelectionControl>
</Window.Resources>
Now how do I put the control in the StackPanel's collection of children ? IntelliSense didn't suggest a <staticresource> element but I still tried:
<ScrollViewer Grid.Row="2" HorizontalScrollBarVisibility="Auto">
<StackPanel VerticalAlignment="Top" HorizontalAlignment="Left">
<StaticResource ResourceKey="GroupOwnerCRDSCode" />
When I run it, I get a "first chance exception of type 'System.InvalidOperationException' occurred in mscorlib.dll - Additional information: Stack empty.".
However, the preview in the Designer is just fine.
Does this approach has any chance of working ? What am I doing wrong ? If it works, will I get a single instance of a control (as identified by the same key) shared between my dialogs ? Or is a new instance created each time I pull from the resources ? What are the alternatives (I see one: put the three strings in resources, not the entire control).
|
|
|
|
|
Jean-Louis Leroy wrote: <layer>If it works, will I get a single instance of a control (as identified by the same key) shared between my dialogs ? Or is a new instance created each time I pull from the resources ?
Neither... Each defined resource is a single instance, but it gets defined once for every window. So each window gets exactly one instance. This means you can use it once per window, but no more than that (Because a UI element can only have one parent).
Jean-Louis Leroy wrote: When I run it, I get a "first chance exception of type 'System.InvalidOperationException' occurred in mscorlib.dll - Additional information: Stack empty.".
I would suggest looking at the call stack, to get a little more information about that exception. Exactly what was it trying to do when it hit that exception? Was it related to the control being in the StackPanel, or was it something inside the FeatureSelectionControl?
Jean-Louis Leroy wrote: What are the alternatives (I see one: put the three strings in resources, not the entire control).
You could also do this by defining styles or templates. Those are intended to be reused (Unlike defining the control itself as a resource). Then, instead of putting a StaticResource directly into a StackPanel, you would do something like this:
<Style x:Key="GroupOwnerCRDSCode" TargetType="local:FeatureSelectionControl">
<Setter Property="Table" Value="GroupOwnerCRDSCode"/>
<Setter Property="Label" Value="Customer Group"/>
<Setter Property="SelectStatement">
<Setter.Value>
SELECT blablabla
</Setter.Value>
</Setter>
</Style>
<StackPanel ...>
<local:FeatureSelectionControl Style="{StaticResource GroupOwnerCRDSCode}"/>
</StackPanel>
|
|
|
|
|
Thanks for the very clear explanations and the suggestion about Styles. It looks like a better idea because - if I'm not mistaken - it will allow me to set other properties like Margin etc.
Out of curiosity I still took a look at the stack trace in my control-in-resource approach. Here it is:
PresentationFramework.dll!System.Windows.Markup.XamlParseException.ThrowException(string message, System.Exception innerException, int lineNumber, int linePosition, System.Uri baseUri, System.Windows.Markup.XamlObjectIds currentXamlObjectIds, System.Windows.Markup.XamlObjectIds contextXamlObjectIds, System.Type objectType) + 0x1bf bytes
PresentationFramework.dll!System.Windows.Markup.XamlParseException.ThrowException(System.Windows.Markup.ParserContext parserContext, int lineNumber, int linePosition, string message, System.Exception innerException) + 0x58 bytes
PresentationFramework.dll!System.Windows.Markup.BamlRecordReader.ReadRecord(System.Windows.Markup.BamlRecord bamlRecord) + 0x75f bytes
PresentationFramework.dll!System.Windows.Markup.BamlRecordReader.Read(bool singleRecord = false) + 0x1c bytes
PresentationFramework.dll!System.Windows.Markup.TreeBuilderBamlTranslator.ParseFragment() + 0xc9 bytes
PresentationFramework.dll!System.Windows.Markup.TreeBuilder.Parse() + 0xf bytes
PresentationFramework.dll!System.Windows.Markup.XamlReader.LoadBaml(System.IO.Stream stream, System.Windows.Markup.ParserContext parserContext, object parent, bool closeStream) + 0xc7 bytes
PresentationFramework.dll!System.Windows.Application.LoadComponent(object component, System.Uri resourceLocator) + 0x16e bytes
> CRRT.exe!CRRT.WindowCounterpartyGroupRiskReview.InitializeComponent() Line 1 + 0xb bytes C#
CRRT.exe!CRRT.WindowCounterpartyGroupRiskReview.WindowCounterpartyGroupRiskReview() Line 29 + 0x8 bytes C#
|
|
|
|
|
Ugh, typical unhelpful Xaml/Baml traces... Really wish Microsoft would put some useful information in there.
The only strategy I know for errors like that, is to just keep commenting out bits of the XAML until it runs successfully, and then narrowing it down from there.
|
|
|
|
|
Ian Shlasko wrote: Ugh, typical unhelpful Xaml/Baml traces... Really wish Microsoft would put some useful information in there.
Really wish Microsoft would give the source code like they used to for the CRT
I'm very happy with the Style-based solution, thanks.
|
|
|
|
|