Atribut DisplayName dari Sumber Daya?

160

Saya memiliki aplikasi lokal, dan saya bertanya-tanya apakah mungkin untuk memiliki DisplayNameproperti model tertentu yang ditetapkan dari Sumber Daya.

Saya ingin melakukan sesuatu seperti ini:

public class MyModel {
  [Required]
  [DisplayName(Resources.Resources.labelForName)]
  public string name{ get; set; }
}

Tapi saya tidak bisa melakukannya, seperti kata kompiler: "Argumen atribut harus berupa ekspresi konstan, ekspresi typeof atau ekspresi penciptaan array tipe parameter atribut" :(

Apakah ada solusi? Saya mengeluarkan label secara manual, tetapi saya membutuhkan ini untuk keluaran validator!

Palantir
sumber

Jawaban:

112

Bagaimana dengan menulis atribut khusus:

public class LocalizedDisplayNameAttribute: DisplayNameAttribute
{
    public LocalizedDisplayNameAttribute(string resourceId) 
        : base(GetMessageFromResource(resourceId))
    { }

    private static string GetMessageFromResource(string resourceId)
    {
        // TODO: Return the string from the resource file
    }
}

yang bisa digunakan seperti ini:

public class MyModel 
{
    [Required]
    [LocalizedDisplayName("labelForName")]
    public string Name { get; set; }
}
Darin Dimitrov
sumber
Ya, saya mengerjakan solusi serupa pagi ini dan itu layak. Kemudian saya menemukan posting ini yang memiliki pendekatan yang sama: adamyan.blogspot.com/2010/02/...
Palantir
23
@Gunder postingnya, di bawah yang satu ini (dengan suara terbanyak), adalah solusi yang jauh lebih baik. Hanya untuk orang-orang yang hanya membaca posting yang Diterima
321X
1
Ini sebenarnya TIDAK bekerja untuk mengakses terjemahan yang berbeda karena akan mengembalikan nilai yang sama untuk semua pengguna apa pun. Simpan resourceid dalam variabel lokal dan ganti DisplayName sebagai gantinya
Fischer
5
Saran untuk TODO: return Resources.Language.ResourceManager.GetString (resourceId);
Leonel Sanches da Silva
4
Anda harus menggunakan: [Tampilan (Nama = "labelForName", ResourceType = typeof (Resources.Resources))] seperti yang dijelaskan di bawah ini ...
Frank Boucher
375

Jika Anda menggunakan MVC 3 dan .NET 4, Anda bisa menggunakan Displayatribut baru di System.ComponentModel.DataAnnotationsnamespace. Atribut ini menggantikan DisplayNameatribut dan menyediakan lebih banyak fungsi, termasuk dukungan lokalisasi.

Dalam kasus Anda, Anda akan menggunakannya seperti ini:

public class MyModel
{
    [Required]
    [Display(Name = "labelForName", ResourceType = typeof(Resources.Resources))]
    public string name{ get; set; }
}

Sebagai catatan tambahan, atribut ini tidak akan berfungsi dengan sumber daya di dalam App_GlobalResourcesatau App_LocalResources. Ini ada hubungannya dengan alat kustom ( GlobalResourceProxyGenerator) penggunaan sumber daya ini. Alih-alih pastikan file sumber daya Anda disetel ke 'Sumber daya tertanam' dan gunakan alat kustom 'ResXFileCodeGenerator'.

(Sebagai catatan tambahan, Anda tidak boleh menggunakan App_GlobalResourcesatau menggunakan App_LocalResourcesMVC. Anda dapat membaca lebih lanjut mengapa hal ini terjadi di sini )

René
sumber
Ini bagus untuk contoh khusus yang digunakan di sini, tetapi ini tidak akan berfungsi untuk mayoritas setter properti dinamis yang menginginkan string.
kingdango
1
Ini bagus ketika kami memiliki semua lokal kami dengan kami sebelum digunakan untuk produksi. Tetapi jika saya ingin memanggil db untuk db string maka pendekatan semacam ini tidak tepat. Juga kerugian dari file sumber daya, adalah bahwa ia membutuhkan kompilasi lagi untuk melakukan perubahan, itu berarti Anda harus merilis versi lain ke klien. Saya tidak mengatakan ini sebagai pendekatan yang buruk, tolong jangan merasa seperti itu
kbvishnu
Hanya masalah: kita harus tahu tipe sumber daya dalam model. Saya punya model DLL dan situs web dalam dua proyek berbeda. Saya ingin dapat menetapkan nama tampilan dalam model dan mengatur jenis sumber daya di situs web ...
Kek
1
Dapatkan Resharper, dan buat file sumber di proyek Anda, dan itu akan secara otomatis mengingatkan dan kemudian membantu Anda untuk memindahkan Nama ke file sumber.
anIBMer
14
Dengan C # 6, alih-alih Name = "labelForName"Anda juga bisa menggunakan Name = nameof(Resources.Resources.labelForName).
Uwe Keim
35

Jika Anda membuka file sumber daya Anda dan mengubah pengubah akses ke publik atau internal itu akan menghasilkan kelas dari file sumber daya Anda yang memungkinkan Anda untuk membuat referensi sumber daya sangat diketik.

Opsi untuk pembuatan kode file sumber daya

Yang berarti Anda dapat melakukan sesuatu seperti ini sebagai gantinya (menggunakan C # 6.0). Maka Anda tidak perlu mengingat apakah nama depan lebih kecil atau lebih tebal. Dan Anda dapat melihat apakah properti lain menggunakan nilai sumber daya yang sama dengan temukan semua referensi.

[Display(Name = nameof(PropertyNames.FirstName), ResourceType = typeof(PropertyNames))]
public string FirstName { get; set; }
Tikall
sumber
apakah ini akan bekerja dengan Winforms? Saya memiliki kelas yang saya tambahkan Tampilan anotasi dari sumber dan saya menggunakan metode GetAttributeFrom dari tautan untuk mendapatkan nama properti tetapi tidak menunjukkan yang dilokalkan!
Dark_Knight
2
Apakah Anda menggunakan atribut.Name atau atribut.GetName () untuk mendapatkan teks yang dilokalkan? Dokumentasi untuk .Name mengatakan "Jangan gunakan properti ini untuk mendapatkan nilai properti Name. Sebaliknya, gunakan metode GetName." msdn.microsoft.com/en-us/library/…
Tikall
Ya, saya menemukan bahwa saya harus menggunakan GetName (). Terima kasih
Dark_Knight
20

Memperbarui:

Saya tahu ini sudah terlambat tetapi saya ingin menambahkan pembaruan ini:

Saya menggunakan Penyedia Metadata Model Konvensional yang dihadirkan oleh Phil Haacked , lebih kuat dan mudah diterapkan lihatlah: ConventionalModelMetadataProvider


Jawaban Lama

Di sini jika Anda ingin mendukung banyak jenis sumber daya:

public class LocalizedDisplayNameAttribute : DisplayNameAttribute
{
    private readonly PropertyInfo nameProperty;

    public LocalizedDisplayNameAttribute(string displayNameKey, Type resourceType = null)
        : base(displayNameKey)
    {
        if (resourceType != null)
        {
            nameProperty = resourceType.GetProperty(base.DisplayName,
                                           BindingFlags.Static | BindingFlags.Public);
        }
    }

    public override string DisplayName
    {
        get
        {
            if (nameProperty == null)
            {
                return base.DisplayName;
            }
            return (string)nameProperty.GetValue(nameProperty.DeclaringType, null);
        }
    }
}

Kemudian gunakan seperti ini:

    [LocalizedDisplayName("Password", typeof(Res.Model.Shared.ModelProperties))]
    public string Password { get; set; }

Untuk tutorial pelokalan lengkap, lihat halaman ini .

Wahid Bitar
sumber
1
+1. Solusi Haack jelas yang paling elegan dibandingkan yang lain di sini. Ini sangat cocok dengan gaya pemrograman berbasis konvensi di ASP.NET MVC dan mudah diimplementasikan melalui perintah nuget tunggal dan satu baris kode di Global.asax.cs.
Nilzor
11

Saya mendapatkan jawaban Gunders dengan App_GlobalResources saya dengan memilih properti sumber daya dan beralih "Alat Kustom" ke "PublicResXFileCodeGenerator" dan membangun tindakan untuk "Sumber Daya Tertanam". Silakan perhatikan komentar Gunders di bawah ini.

masukkan deskripsi gambar di sini

Bekerja seperti pesona :)

Magnus Karlsson
sumber
Ini menghasilkan file sumber daya yang akan dikompilasi seperti jika Anda telah menambahkan file sumber daya di luar App_GlobalResources. Jadi file sumber daya Anda tidak lagi berperilaku sebagai file sumber daya "App_GlobalResources", yang benar-benar baik-baik saja. Tetapi Anda harus sadar akan hal itu. Jadi Anda tidak lagi memiliki "manfaat" menempatkan file sumber daya di App_GlobalResources. Anda bisa saja meletakkannya di tempat lain.
René
5
public class Person
{
    // Before C# 6.0
    [Display(Name = "Age", ResourceType = typeof(Testi18n.Resource))]
    public string Age { get; set; }

    // After C# 6.0
    // [Display(Name = nameof(Resource.Age), ResourceType = typeof(Resource))]
}
  1. Tentukan ResourceType atribut sehingga mencari sumber daya
  2. Tentukan Nama atribut yang digunakan untuk kunci sumber daya, setelah C # 6.0, Anda dapat menggunakan nameofuntuk dukungan mengetik yang kuat alih-alih mengkode kunci dengan keras.

  3. Atur budaya thread saat ini di controller.

Resource.Culture = CultureInfo.GetCultureInfo("zh-CN");

  1. Atur aksesibilitas sumber daya ke publik

  2. Tampilkan label dalam cshtml seperti ini

@Html.DisplayNameFor(model => model.Age)

code4j
sumber