Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe
WPF question. I have a MainWindow that has a FooControl embedded in it, declared in XAML as a ContentControl. The XAML for the ContentControl, taken from MainWindow.xaml:
code:
<ContentControl Grid.Row="3"
                Grid.Column="1"
                Content="{Binding FooControlViewModel}"
                KeyboardNavigation.IsTabStop="False"
                Margin="2"
                />
FooControlViewModel, the type, is declared in the Resources section like so:
code:
<Window.Resources>
  <DataTemplate DataType="{x:Type local:FooControlViewModel}">
    <local:FooControl />
  </DataTemplate>
</Window.Resources>
(I expect this technique has a name but I don't know what it is.)

There is a method on the FooControl itself that I want to call from the code-behind of the Main Window. (The method on the Main Window is an on-click handler for an image. The method I want to call on the FooControl gives keyboard focus to a button within the FooControl.)

Now, I can associate the MainWindow with the MainWindowViewModel because it instantiates its own view-model on being constructed. I can associate the MainWindowViewModel with the FooControlViewModel, because the one is a property of the other and is instantiated by it. But I don't know how to get hold of the FooControl object from any of the other 3. So I can't figure out a way to call the method.

Actually, that's a lie. I can call the method perfectly easily: this is a quick and dirty operation and both MainWindow and FooControl will only ever be instantiated once, so I made the FooControl a singleton with a public static "Instance" property (populated by the constructor), and I'm calling the method on the instance. But I would like to know what the proper answer is.

Adbot
ADBOT LOVES YOU

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!
Fru fru hippy question about WPF: is it really pushing composition over inheritance? I've been doing some refactor where that is looking like the more natural thing to do now. I mean, it's what I would want to do, but I couldn't really figure out how to do it in its syntax. I'm just asking to help set up my sense of smell when doing stuff.

Hammerite posted:

There is a method on the FooControl itself that I want to call from the code-behind of the Main Window. (The method on the Main Window is an on-click handler for an image. The method I want to call on the FooControl gives keyboard focus to a button within the FooControl.)

I've tried to do similar a few times already and WPF will punch me across the face for trying it. My spidey sense sees it in two parts.

First, see if the image can send a command to the view model for when it's selected instead of trying to get the on-click event. You'd be done there but then you want to have the code-behind do something while you're inside the code for the view model. WPF will then punch you across the face there instead.

The second part takes care of that trap. Create a property in the view model and implement INotifyPropertyChanged so the runtime will listen to it. Kick that off when your image's command is invoked. If the view model doesn't otherwise associate the image and the button, pass the button as an argument to the command. You'd then bind this property for the selected button in the XAML.

...or something. gently caress, just thinking about it makes me want to take a nap.

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe
I don't think you can directly assign a command to the "image is clicked" event. You can put the image on a button or something and do that. Regardless, I want to know where on the image the user clicked (I ought to have said this in the original post), so AFAIK I really do need a click event and not a command - the position on the image can be calculated using properties of the MouseButtonEventArgs.

Rocko Bonaparte posted:

The second part takes care of that trap. Create a property in the view model and implement INotifyPropertyChanged so the runtime will listen to it. Kick that off when your image's command is invoked. If the view model doesn't otherwise associate the image and the button, pass the button as an argument to the command. You'd then bind this property for the selected button in the XAML.

The button is within FooControl, there is (AFAIK) no way to reference it from MainWindow's XAML so I don't think I can pass it as a command parameter even if I could use a command to do what I want to do (which as remarked above, I don't think I can).

Both ViewModels implement INotifyPropertyChanged.

If there's a way to cause a button to grab the keyboard focus in response to the PropertyChanged events of its owning control's DataContext, though, then that potentially solves my problem. I don't know that there is.

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!
Yeah okay that is more elaborate. At least for the image, have you tried using InputBinding's mouse clicking stuff? I never tried it but I want to think you can bind a command there and then pass along the control too.

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe
I found the answer. When the FooControl loads for the first time, 3 things happen in order:

  • The constructor is called
  • An Initialized handler is called, if you defined one
  • A Loaded handler is called, if you defined one

For the first two, the DataContext is null. For the Loaded event, it's been set. So you can "say hello" to the FooControlViewModel at that point*, and from then on you've achieved your link. It's quite tortuous though, because when the user clicks on the image, my MainWindow code-behind has to call the MainWindow ViewModel, which has to call the FooControlViewModel, which has to call back to the FooControl code-behind.

* When I do things like this I like to just provide the viewmodel with an Action<> or Func<> rather than a reference to the window itself, since that's the minimal amount of information needed in order to accomplish the goal. In this case the viewmodel will have an Action called something like "GiveFocusToButton". The Loaded event-handler in the code-behind will provide that Action to the viewmodel.

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!
It doesn't smell like the "right way" (lol) but it's not like I was particularly helpful either.

I think I saw some StackOverflow stuff that was unrelated were somebody was trying to hit those handlers too and had a bunch of trouble. It looks like that was more their problem than anything else. It didn't help that it was one of those million "something something doesn't work" kind of posts where they don't even state how they expected it to work in the first place.

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!
My turn to ask WPF stuff again: how might I force a list view to show five buttons it contains without any scrolling? I'd want it to scale with the window size too. I'm trying to reuse my general user control for some presentation consistency. Otherwise, I'd just lay out those buttons explicitly in it's own XAML.

I have a custom style for the list view that contains the buttons. At some point, I outright set the height for the buttons to an absolute number of pixels, but that gets screwy with different window sizes. If you wanted to see the junk I'm setting in that style:

code:
                    <!-- Stretches the save buttons to fill up the ListView... mostly. There seems to still be padding on either side? -->
                    <Style x:Key="ListViewButtonStyle" TargetType="ListViewItem">
                        <Setter Property="HorizontalContentAlignment" Value="Stretch" />
                        <Setter Property="VerticalContentAlignment" Value="Stretch" />
                        <!-- <Setter Property="Height" Value="74" /> -->
                        <!-- Minimum 64 pixels for button plus 5 pixel padding at top and 5 pixel padding at bottom
                             Further note: this doesn't really work for showing 5 buttons at the demo 640x480 resolution. The bottom button goes below 
                             the bottom bar. A height of 62 kind of works.
                        -->
                        <!-- This suppressed event prevents clicking the bottommost button causing it to scroll into view instead of firing the command. 
                             I'm not entirely convinced this is over. My hunch is this might prevent scrolling with a gamepad too. -->
                        <EventSetter Event="RequestBringIntoView" Handler="MuteButtonBringIntoView"/>
                    </Style>
I figure what I'd actually need are different styles based on how I'm using the control. I want to fully display five buttons for my main menu without scrolling, but I care less about that--and definitely want to have scrolling--when looking at buttons representing many different saves. I'm just focusing on the main menu here. Is there something I can consistently set to preserve 5 visible buttons without scrolling? My attempt at a hack here will be try to bind some calculation based off of the list's height. I was thinking there was a better way.

Edit: I ended up doing binding shenanigans with a value converter. The button heights are set to 1/5 of the listview's height minus 1 for its padding. Or rather, it takes the number of buttons I want to use as an argument, but the argument is 5. It wasn't too huge of a chore to do it but I was surprised I had to go through all that.

Also, Googling for how to disable a horizontal scrollbar in a list view was a real treat. I guess it was a huge hack for a decade or something until they just added a simple property for it.

Rocko Bonaparte fucked around with this message at 22:09 on Dec 1, 2020

LongSack
Jan 17, 2003

Question about layering, I guess. So, in theory, and following the “single responsibility” principle, I should put all “business logic” in a layer between the ViewModels and some data access layer.

This brings to mind a scenario where the ViewModel exposes a “Delete” command. The user clicks the delete button, only to be told (by the logic layer) that the item can’t be deleted because it has associated other items so deleting it would violate referential integrity.

As an end user, I might reasonably ask, if the item can’t be deleted, why was the delete button enabled?

This scenario poses to me a conundrum: how much business logic do I put in my ViewModels? As an end user, I completely understand that if an item can’t be deleted, then there either shouldn’t be a delete button, or it should be disabled.

At the same time, I respect the separation of concerns.

I suppose that I could have fields on my model classes like CanBeDeleted, but as the number of scenarios grows, this becomes unwieldy.

Is there a good solution to this? TIA

Bruegels Fuckbooks
Sep 14, 2004

Now, listen - I know the two of you are very different from each other in a lot of ways, but you have to understand that as far as Grandpa's concerned, you're both pieces of shit! Yeah. I can prove it mathematically.

LongSack posted:

Question about layering, I guess. So, in theory, and following the “single responsibility” principle, I should put all “business logic” in a layer between the ViewModels and some data access layer.

This brings to mind a scenario where the ViewModel exposes a “Delete” command. The user clicks the delete button, only to be told (by the logic layer) that the item can’t be deleted because it has associated other items so deleting it would violate referential integrity.

As an end user, I might reasonably ask, if the item can’t be deleted, why was the delete button enabled?

This scenario poses to me a conundrum: how much business logic do I put in my ViewModels? As an end user, I completely understand that if an item can’t be deleted, then there either shouldn’t be a delete button, or it should be disabled.

At the same time, I respect the separation of concerns.

I suppose that I could have fields on my model classes like CanBeDeleted, but as the number of scenarios grows, this becomes unwieldy.

Is there a good solution to this? TIA

In ICommand interface, there is a CanExecuteCommand(). If you implement CanBeDeleted in that method itself, then the button will enable/disable itself when RaiseCanExecuteChanged() fires (probably a property for a setting in your view model.) Exposing CanBeDeleted on your model class and checking it in CanExecuteCommand is how I handled this situation when it came up for me.

LongSack
Jan 17, 2003

Bruegels Fuckbooks posted:

In ICommand interface, there is a CanExecuteCommand(). If you implement CanBeDeleted in that method itself, then the button will enable/disable itself when RaiseCanExecuteChanged() fires (probably a property for a setting in your view model.) Exposing CanBeDeleted on your model class and checking it in CanExecuteCommand is how I handled this situation when it came up for me.

Yeah, that’s the only thing I could come up with. Just wanted to see if I was missing something (always a possibility). Thanks!

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!
A little bit of WPF XAML stuff: how can I draw a rectangle inside a grid, and have a plus sign inside that rectangle, and have the plus sign centered? I would have thought using center for horizontal and vertical alignment would do it, but it's actually a little below center.

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe

Rocko Bonaparte posted:

A little bit of WPF XAML stuff: how can I draw a rectangle inside a grid, and have a plus sign inside that rectangle, and have the plus sign centered? I would have thought using center for horizontal and vertical alignment would do it, but it's actually a little below center.

Is it text, or an image of a plus sign? Small snippets of text can be a bit of a pain in the arse to get perfectly vertically centered, not just in WPF but in other layout engines too, because the text rendering engine might not have the same idea about what vertically centered actually means. Text baselines, and all that sort of thing. An image is probably easier

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!

Hammerite posted:

Is it text, or an image of a plus sign? Small snippets of text can be a bit of a pain in the arse to get perfectly vertically centered, not just in WPF but in other layout engines too, because the text rendering engine might not have the same idea about what vertically centered actually means. Text baselines, and all that sort of thing. An image is probably easier

Yeah it's just text and I think you just sealed the deal on me using some kind of image instead. I can totally see a plus sign being off-center based on the font or whatever. Actually, I'll maybe delve into some of the drawing stuff in WPF for giggles but it'll probably become an image in the end.

Drastic Actions
Apr 7, 2009

FUCK YOU!
GET PUMPED!
Nap Ghost
I opened up a new Project.log for a SA Forum Reader app I'm starting, Awful.NET.

Right now it's a Xamarin.Forms Mobile app, with intentions to expand it out to desktop and other platforms. If anyone wants to screw around on a Xamarin app, or on a new .NET library for accessing SA, let me know. :)

beuges
Jul 4, 2005
fluffy bunny butterfly broomstick

Drastic Actions posted:

I opened up a new Project.log for a SA Forum Reader app I'm starting, Awful.NET.

Right now it's a Xamarin.Forms Mobile app, with intentions to expand it out to desktop and other platforms. If anyone wants to screw around on a Xamarin app, or on a new .NET library for accessing SA, let me know. :)

Will you be porting this to MAUI once it's practical to do so?

Drastic Actions
Apr 7, 2009

FUCK YOU!
GET PUMPED!
Nap Ghost

beuges posted:

Will you be porting this to MAUI once it's practical to do so?

Most likely, but it'll probably still use XAML as the baseline for its views rather than all code and switch to MVU.

Not because I'm against it, but I work at Microsoft on the underlying XAML Hot Reload code. So this project is a way for me to dogfood the tools I'm writing and file bugs against myself to fix them. Also, XAML is what the majority of .NET Devs know and use so I want to stick with something that others can contribute to easily. That's also (mostly) why it's in C# rather than F# :v:.

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!
How might I attach a custom view source to a ListView in XAML? It looks like I can almost do all of this with just XAML without a custom comparer or anything, but I get stuck right at the end.

I have this resource:
code:
<CollectionViewSource x:Key="sortedSaveButtons" Source="{Binding SaveButtonData}">
	<CollectionViewSource.SortDescriptions>
		<scm:SortDescription PropertyName="Modified" Direction="Descending"/>
		<scm:SortDescription PropertyName="Created" Direction="Descending"/>
		<scm:SortDescription PropertyName="Name" Direction="Descending"/>
	</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
...and this ListView:
code:
<ListView ScrollViewer.HorizontalScrollBarVisibility="Disabled" Name="ButtonList" Grid.Column="1"
	Background="{x:Null}" ItemContainerStyle="{StaticResource ListViewButtonStyle}"
	ItemsSource="{StaticResource ResourceKey=sortedSaveButtons}"
	ItemTemplateSelector="{Binding RelativeSource={RelativeSource AncestorType=UserControl},Path=ButtonTemplateSelector}"
	SelectedIndex="{Binding SelectedButtonIndex, diag:PresentationTraceSources.TraceLevel=High}"
	SelectedItem="SelectedButtonData">                    
</ListView>
...but it's mad about setting the ItemSource to be the sorted collection view source. It's not an IEnumerable. Is this a thing I can even do this way somehow? Older stuff talks a lot about having to set this up in the code-behind, which feels like a deprecated thing.

As a side thing, I might actually have to use a custom Comparer<T> due to some shenanigans and I couldn't figure out how to specify that in XAML either. That was even worse.

I'm trying to avoid having my buttons call back to the view model to sort the collection after operations happen.

Boz0r
Sep 7, 2006
The Rocketship in action.
Is there any way to get a unique identifier of a Func? We want to do some caching, but right now the caller has to send a string key along with a Func.

insta
Jan 28, 2009
Can't you use the Func itself as a key? I know you can with a Dictionary.

GI_Clutch
Aug 22, 2000

by Fluffdaddy
Dinosaur Gum
I have a question about dependency injection and .NET. I've tried Googling, but haven't had much luck finding an answer. I just keep finding variations of the same basic examples that aren't any more complicated than what I've already done. I'm still very new to the subject, only really messing around with it lightly from building a few simple REST services. I get the general gist of it, and it seems really straight forward in something like ASP.NET where the server has to magic an instance of a controller into existence when a request comes in and it creates the dependencies down the line without newing them up explicitly through code.

But what about in something like a larger application where an end user is driving the experience and things may not be needed? For example, let's say I built some kind of swiss army knife console application with a variety of utilities in it. The user is presented with with a menu to select the task they would like to perform. Each of the various tasks does something completely different with its own set of dependencies (maybe they hit different database models, work with Windows services, read/build flat files, etc.) In this situation, there is a single entry point. I don't know what is going to be needed until the user makes a selection.

I am pretty sure the option is not to inject numerous items into the base class's constructor. That would result in a lot of objects being instantiated that may not be necessary as the user might only end up performing one task out of a possible dozen. Another thought was that maybe I need a reference to the service provider and then use ActivatorUtilities (or something) to create instances when needed, but apparently passing around a reference to the service provider is an anti-pattern. It also feels like that could throw a wrench into testing as now my methods are requiring a service provider. Then I thought that maybe I do just need to "new" some of these items up, but what about when they need something I was planning to inject? I'd end up needing to pass them into the constructor, and well, then I need to new those up as well, and I'm way off track.

I'm sure there are some accepted patterns to use here, but again, I'm not having any luck finding them. I either don't know what I'm trying to search for or my overall understanding is lacking. Any suggestions?

Just-In-Timeberlake
Aug 18, 2003

GI_Clutch posted:

For example, let's say I built some kind of swiss army knife console application with a variety of utilities in it. The user is presented with with a menu to select the task they would like to perform. Each of the various tasks does something completely different with its own set of dependencies (maybe they hit different database models, work with Windows services, read/build flat files, etc.) In this situation, there is a single entry point. I don't know what is going to be needed until the user makes a selection.

lol'ing hard because I made an application for work that does exactly this and named it Swiss Army Knife (SAK because I'm a child :imunfunny: )

I have no real insight as I made it out of a necessity to get some things quickly done so I'm sure anybody looking at the code would consider it a mess.

distortion park
Apr 25, 2011


You should just be able to do stuff "normally" in a console app, instances of dependencies will only be created as required. Tbh I wouldn't worry about it unless it was actually a performance problem if you've managed to set it up so that you build the world everytime anyway.

e: I see what you mean actually now thinking about it. I think if it did turn out to be a problem to build everything (test it, it probably isn't), then in that case separating out the argument parsing from the rest of the app and then calling the service provider to get the object type you needed to handle those options would be fine. So you shouldn't be passing the service provider anywhere except into some big switch block to handle the args.

distortion park fucked around with this message at 20:06 on Dec 7, 2020

distortion park
Apr 25, 2011


The other option is the "Factory" pattern ofc, but that's probably more effort than it's worth in this case

raminasi
Jan 25, 2005

a last drink with no ice
Some frameworks (e.g. Autofac) allow dependencies to be configured as “lazy,” so they’re only instantiated once they’re needed. A concept that might help you is “lifetimes” - in normal web applications, per-request lifetime is usually the only one used so the concept can get glossed over in introductory tutorials, but there are other lifetimes to think about.

New Yorp New Yorp
Jul 18, 2003

Only in Kenya.
Pillbug

pointsofdata posted:

Tbh I wouldn't worry about it unless it was actually a performance problem

This. Don't create a problem where none exists.

GI_Clutch
Aug 22, 2000

by Fluffdaddy
Dinosaur Gum
Thanks for the suggestions. Thankfully, I'm not running into this problem at this point. I'm just trying to get into the habit of doing things properly. I really didn't touch .NET core until earlier this year because a lot of my code integrates with a third party system that is Framework based and only provided .NET Standard libraries within the past two months. Of course, they only work on the very latest build which none of our customers are on...

Otherwise I'm typically building relatively simple console apps for backend integrations. For example, my upcoming project boils down to a console app that just polls a folder for files, parses the filenames, grabs additional data from a DB based on the metadata in the filenames, and then spits out a flat file with the expanded data so they can be ingested into a document management system. So, a very linear path where my question doesn't even come into play. Just figured I'd see if I could wrap my head around a larger solution.

insta
Jan 28, 2009
Thirding "don't worry about it". Your constructors aren't doing anything except storing a reference to other items anyway, no actual work happens in them (riiiiiiight? :colbert:), and references are cheap.

Cuntpunch
Oct 3, 2003

A monkey in a long line of kings

insta posted:

Thirding "don't worry about it". Your constructors aren't doing anything except storing a reference to other items anyway, no actual work happens in them (riiiiiiight? :colbert:), and references are cheap.

https://blog.ploeh.dk/2011/03/04/Composeobjectgraphswithconfidence/

Build your object graph immediately, and don't worry about it. If you're going to have new objects, they should probably be popping out of Factories that the class needing them depends on.

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!
What's up with WPF visibility being an enum? I'm guessing it's some artifact from the early days that didn't get updated with the whole view model paradigm. I clearly am not the only person that had set up a boolean to determine whether to show or hide something.

I see the visibility actually has three states so a boolean doesn't fully account for it, but I was kind of figuring there's be a basic show/noshow built in. That was just bizarre to deal with, and also makes me add additional stuff to expose a Visibility value instead.

fankey
Aug 31, 2001

Rocko Bonaparte posted:

What's up with WPF visibility being an enum? I'm guessing it's some artifact from the early days that didn't get updated with the whole view model paradigm. I clearly am not the only person that had set up a boolean to determine whether to show or hide something.

I see the visibility actually has three states so a boolean doesn't fully account for it, but I was kind of figuring there's be a basic show/noshow built in. That was just bizarre to deal with, and also makes me add additional stuff to expose a Visibility value instead.

Use a BooleanToVisibilityConverter in your XAML.

power crystals
Jun 6, 2007

Who wants a belly rub??

Rocko Bonaparte posted:

What's up with WPF visibility being an enum? I'm guessing it's some artifact from the early days that didn't get updated with the whole view model paradigm. I clearly am not the only person that had set up a boolean to determine whether to show or hide something.

I see the visibility actually has three states so a boolean doesn't fully account for it, but I was kind of figuring there's be a basic show/noshow built in. That was just bizarre to deal with, and also makes me add additional stuff to expose a Visibility value instead.

Having both "Hidden" (not rendered but occupies space) and "Collapsed" (not rendered, size 0x0) is legitimately useful sometimes. Most projects I've worked on have used both at least once. I've always just done it by exposing e.g. a "FooButtonVisibility" property of type Visibility on the viewmodel and setting that as appropriate but also I'm pretty sure I only barely understand MVVM so I have no idea if that's best practice or not.

epswing
Nov 4, 2003

Soiled Meat

Rocko Bonaparte posted:

What's up with WPF visibility being an enum? ... I see the visibility actually has three states so a boolean doesn't fully account for it

Asked and answered!

power crystals posted:

I've always just done it by exposing e.g. a "FooButtonVisibility" property of type Visibility on the viewmodel and setting that as appropriate but also I'm pretty sure I only barely understand MVVM so I have no idea if that's best practice or not.

Yeah that'll work in a pinch. I sometimes do this for expediency while coding. But I agree with fankey that the "correct" way in WPF is to write a reusable IValueConverter, in this case to

epswing fucked around with this message at 19:52 on Dec 8, 2020

power crystals
Jun 6, 2007

Who wants a belly rub??

epalm posted:

Yeah that'll work in a pinch. I sometimes do this for expediency while coding. But I agree with fankey that the "correct" way in WPF is to write a reusable IValueConverter

Honest question, how does this work when the visibility is more than a simple boolean, i.e. "visible if Foo is true and Bar is > 0, otherwise collapsed"? Am I supposed to write my own IMultiValueConverter? That seems like more work than a viewmodel property.

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe

power crystals posted:

Honest question, how does this work when the visibility is more than a simple boolean, i.e. "visible if Foo is true and Bar is > 0, otherwise collapsed"? Am I supposed to write my own IMultiValueConverter? That seems like more work than a viewmodel property.

I think it would be normal to expose a getter-only Boolean "IsVisible" property on your view-model whose getter contains that logic.

In principle there is no difference between having a property of type Boolean converted to a Visibility, and just having a property of type Visibility. But usually when you have something that's conditionally displayed, your use case doesn't require its Visibility to be potentially able to take all 3 Visibility enum values. You just have 2 of them that you want it to take, and those are usually "Visible" and "Collapsed". This can be simplified to "is it shown or not", hence a Boolean, which is easy for someone looking at the code to understand at a glance.

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe
i.e.

code:
public bool IsVisible
    => Foo && Bar > 0;

public Visibility Visibility
    => (Foo && Bar > 0) ? Visibility.Visible : Visibility.Collapsed;
No difference (except that if you bind to the first one then you need BooleanToVisibilityConverter)

amotea
Mar 23, 2008
Grimey Drawer
^ This is also one of the areas in which WPF XAML is lacking IMO, the Binding expressions are pretty bare bones and don't support any real logic. Probably would have been implemented if WPF development wasn't stalled for 10 years.

In the past we used this thing: https://github.com/kentcb/WPFConverters which supports arbitrary expressions in XAML, but it's kind of verbose and still meh.

Nowadays I would recommend https://www.reactiveui.net/ for UI stuff. It sort of has its own paradigm in which UI bindings are always defined in code, so you can pretty much do anything you like.

GoodCleanFun
Jan 28, 2004

Hammerite posted:

i.e.

code:
public bool IsVisible
    => Foo && Bar > 0;

public Visibility Visibility
    => (Foo && Bar > 0) ? Visibility.Visible : Visibility.Collapsed;
No difference (except that if you bind to the first one then you need BooleanToVisibilityConverter)

I bind directly to the Visibility property. Sure it is three state, but there is no requirement to use Visibility.Hidden. No need for an additional property or converter.

epswing
Nov 4, 2003

Soiled Meat
I think the decision to use a VM Visibility property vs an IValueConverter has several considerations.

(A) Is this logic is reusable elsewhere, with other VMs and XAML views? If so, you probably want a converter.

(B) Who do you want to make "responsible" for UI visibility? If this VM used by more than one XAML view, some of which need visibility switches, some of which don't, it doesn't sound like the VM should be responsible for visibility, and you probably want a converter.

(C) How simple do you want to keep your VM? Pushing this logic out to a converter means your VM can be simpler (just a bool), and doesn't need to reference UI-related libraries and properties, which arguably makes testing your VM easier.

That said, writing a converter class and hooking it up in XAML is more work that just writing a quick readonly getter property in your VM. I tend to write VM properties until it gets unwieldy (1 or 2 is fine, 10 starts to smell), and I start pushing them into converters as necessary to keep things simple, maintainable, and testable.

epswing fucked around with this message at 16:49 on Dec 9, 2020

LongSack
Jan 17, 2003

power crystals posted:

Honest question, how does this work when the visibility is more than a simple boolean, i.e. "visible if Foo is true and Bar is > 0, otherwise collapsed"? Am I supposed to write my own IMultiValueConverter? That seems like more work than a viewmodel property.

Multi value converters aren’t that much more work than normal value converters. Here’s one I use to convert two doubles (space used, and quota) to a tooltip in the form of “xxx of xxx used”:
C# code:
    public sealed class DoublesToTooltipConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type t, object parm, CultureInfo lang)
        {
            if (values.Length != 2 || values.Any(x => x == DependencyProperty.UnsetValue))
            {
                return null;
            }
            if (!(values[0] is double v1) || !(values[1] is double v2))
            {
                return null;
            }
            string p1 = Tools.Normalize(v1);
            string p2 = Tools.Normalize(v2);
            if (v2 == double.MaxValue)
            {
                return $"{p1} used";
            }
            return $"{p1} used out of {p2}";
        }

        public object[] ConvertBack(object value, Type[] t, object parm, CultureInfo lang)
        {
            throw new NotImplementedException();
        }
    }
Note that Normalize takes a double and returns a string “xx.xx KB” or MB or GB or TB

Adbot
ADBOT LOVES YOU

CapnAndy
Feb 27, 2004

Some teeth long for ripping, gleaming wet from black dog gums. So you keep your eyes closed at the end. You don't want to see such a mouth up close. before the bite, before its oblivion in the goring of your soft parts, the speckled lips will curl back in a whinny of excitement. You just know it.
So I'm playing with .NET 5 now that it's out, and you can make .dbml files again, but there's no System.Data.Linq, so as far as I can tell, they don't frigging do anything.

Do I really have to go back to data connections, writing every query out, and assigning each result to a variable manually? Because I can, but man, I don't wanna.

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply