Open ali50m opened 5 months ago
@ali50m WPF doesn't prevent you from adding DI mechanisms to converter.
@lindexi It`s hard to do it natively, without the help of static service locator.
Converters are only interface contracts, so it shouldn't be difficult to use them with any DI framework. If you need to locate an instance, you could do it using custom markup extension.
@miloush I think it's hard to bind a Dependency Property (WPF's soul IMO) inside a MarkupExtension, right?
Can you give some example on how you want the XAML or code look like? You can certainly bind DP from a ME, that's what the Binding ME does...
@miloush
for example, I have a converter like below, which is used to perform some converting job on a AccountGroup record and show the translation information on it. I have to use the Ioc.Default from CommunityToolkit to get the service.
namespace Device.Converters;
internal sealed class AccountGroupAuthorityMenuTranslationsConverter : IValueConverter
{
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value is IAccountGroup accountGroup)
{
var service1 = Ioc.Default.GetRequiredService<IAccountGroupTranslationService>();
var translation = service1.GetConfigTranslation(accountGroup);
if (translation is { })
return translation;
var service2 = Ioc.Default.GetRequiredService<ITranslationsService>();
translation = service2.GetLocalTranslation(accountGroup.Translations);
return translation;
}
throw new Exception($"the input [{value}], is not {nameof(IAccountGroup)}");
}
public object ConvertBack(
object? value,
Type targetType,
object? parameter,
CultureInfo culture
)
{
return Binding.DoNothing;
}
}
<TextBlock Text="{Binding AccountGroupMenuService.SelectedAccountGroup, Converter={converters2:AccountGroupAuthorityMenuTranslationsConverter}}" />
In ASP.net, getting injected service is quite easy. I can even use code like app.MapGet("/", (IFoo service) => service.Bar()));
to get service.
So if I understand correctly, picking MEF as an example, you would rather have
[Export(typeof(AccountGroupAuthorityMenuTranslationsConverter))]
class AccountGroupAuthorityMenuTranslationsConverter : ConverterBase
{
[Import]
IAccountGroupTranslationService _groupTranslationService;
[Import]
ITranslationsService _translationService;
...
}
And have that somehow be used in XAML, correct? If you do that, an instance will be created with all the imports and then you can request it from the container in the markup extension.
For example, for {Binding Converter={Inject AccountGroupAuthorityMenuTranslationsConverter}}
and in the markup extension's ProvideValue, you would get it from the container. You don't even have to do any binding as far as I can tell, because it is a property on the binding ME.
@miloush Sorry if I didn`t make my question clearly. I modified my code a little to use IValueConverter instead (I copied my source code directly before).
As you see, I have to use 3rd library to get instance from Ioc container, and then use it in the converter. That`s my whole pain point. I believe dependency injection is quite important in nowadays software development world. Morden WPF should have a way to natively bring DI into converter.
Same point though, why wouldn't what I suggested work? Which library are you using?
Otherwise this is duplicate of #499
@miloush I can't fully understander your previous suggestion. I try to made a converter MarkupExtension like below, but still no luck about how to get the injected service. Could you help to make it clear for me? Thanks!
internal sealed class AccountGroupValueToTranslationConverter2 : MarkupExtension, IValueConverter
{
[Import] // Where is this attribute from?
IAccountGroupTranslationService _groupTranslationService;
[Import]
ITranslationsService _translationService;
private AccountGroupValueToTranslationConverter2? _converter;
private IAccountGroupTranslationService? _service1;
private ITranslationsService? _service2;
public override object ProvideValue(IServiceProvider serviceProvider)
{
_service1 = serviceProvider.GetRequiredService<IAccountGroupTranslationService>(); // can`t get the service from sp
_service2 = serviceProvider.GetRequiredService<ITranslationsService>();
_converter = new AccountGroupValueToTranslationConverter2();
return _converter;
}
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value is not int accountValue)
return "Wrong type!";
var accountGroup = AccountGroup.GetMenu(accountValue);
var translation =
_service1!.GetConfigTranslation(accountGroup)
?? _service2!.GetLocalTranslation(accountGroup.Translations);
return translation;
}
public object ConvertBack(
object? value,
Type targetType,
object? parameter,
CultureInfo culture
)
{
return Binding.DoNothing;
}
}
Import and Export attributes are part of MEF. If you are not using that framework, the example is not very helpful to you. Which DI library are you using?
@miloush it's Microsoft.Extensions.DependencyInjection
.
@ali50m
My friend @h82258652 wrote the Chinese blog that provides a tutorial on injecting dependencies into the Converter, please refer to https://www.cnblogs.com/h82258652/p/12625081.html , and thank you @h82258652
And you can use the translator to translate the blog.
@lindexi
The blogger's idea is basically the same as what I mentioned before, using a static service locator. I think he can also directly call the static service locator directly in the converter to get the desired service then without adjusting the ME ConverterType in xaml. I used Ioc.Default, the service locator from the Mvvmtoolkit, basically the same working way.
I still hope that Microsoft can officially add DI support to the converter, just like ASP.NET has done.
Converter works like a birdge between the origin data and UI presentation. It is a very important feature in WPF world. Please consider adding DI support to converter or create new one with DI supported.