- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
当前,我的应用程序的用户设置存储在此默认目录中:
C:\Users\{User Name}\AppData\Roaming\{Company Name}\{Assembly Name}.vshos_Url_{Hash}\{Assembly Version}
C:\Users\{User Name}\AppData\Roaming\{Assembly Name}
最佳答案
存在该命名约定,以便NET可以确保已加载正确的设置。由于您已放弃了对NET Framework / VB应用程序框架的设置进行管理的控制,因此它还负责确保应用程序正在加载正确的设置集。在这种情况下,证据散列用于将一个WindowsApplication1
与另一个唯一地标识。I know this is possible to acchieve, because I've seen much .NET applications that can store its userconfig file in a custom Roaming folder
有可能,但我不确定所有内容是否都符合您的结论。我非常严重地怀疑,许多应用程序使用自定义设置类可以更轻松地将XML文件保存到该位置时,会麻烦地实现自定义提供程序。
简单的解决方案
编写自己的用户选项类,然后自己进行序列化。例如,可以使用Shared / static方法以很少的代码反序列化类(这恰好使用JSON):
Friend Shared Function Load() As UserOptions
' create instance for default on new install
Dim u As New UserOptions
If File.Exists(filePath) Then
' filepath can be anywhere you have access to!
Dim jstr = File.ReadAllText(filePath)
If String.IsNullOrEmpty(jstr) = False Then
u = JsonConvert.DeserializeObject(Of UserOptions)(jstr)
End If
End If
Return u
End Function
UOpt = UserOptions.Load()
SettingsProvider
允许您更改处理,保存和加载设置的方式,包括更改文件夹位置。
SettingsProvider
进行对话以指定文件夹的方法。提供者需要能够在内部进行工作,当然必须保持一致。
AppData\Roaming
中,但是写不合格的本地设置将是不负责任的。区分它们是不应该为了消除文件夹名称中的证据哈希而牺牲的功能。
Setting
设置为
Roaming
或
Local
值:在“设置编辑器”中选择“设置”,打开“属性”窗格-将
Roaming
更改为True。
SettingsProvider
的几个问题中已经达成共识,以将本地和漫游保存到同一文件中的不同部分。这非常有意义-比从2个文件加载更简单-因此使用的XML结构为:
<configuration>
<CommonShared>
<setting name="FirstRun">True</setting>
<setting name="StartTime">15:32:18</setting>
...
</CommonShared>
<MACHINENAME_A>
<setting name="MainWdwLocation">98, 480</setting>
<setting name="myGuid">d62eb904-0bb9-4897-bb86-688d974db4a6</setting>
<setting name="LastSaveFolder">C:\Folder ABC</setting>
</MACHINENAME_A>
<MACHINENAME_B>
<setting name="MainWdwLocation">187, 360</setting>
<setting name="myGuid">a1f8d5a5-f7ec-4bf9-b7b8-712e80c69d93</setting>
<setting name="LastSaveFolder">C:\Folder XYZ</setting>
</MACHINENAME_B>
</configuration>
<NameSpace>.My.MySettings
节点可能会有一些价值,但是我不确定它的作用是什么。
SerializeAs
元素,因此删除了它。
My.Settings.Upgrade
不会发生任何事情。即使它是
Settings
方法,但实际上是
ApplicationSettingsBase
中的内容,因此不涉及您的提供程序。
Foo
值为
Decimal
不能与新的
Foo
用作
Size
。如果您从根本上更改类型,事情仍然会很糟糕。不要那样做
Point
或
Size
等复杂类型
System.Configuration
的引用
Imports System.Configuration
Public Class CustomSettingsProvider
Inherits SettingsProvider
End Class
<> View Code
按钮:
My.MySettings
。您还可以通过在“属性”窗格中键入提供程序名称来按设置指定自定义提供程序,然后跳过此步骤的其余部分。我没有对此进行测试,但是它应该是这样工作的。
MySettings
关联:
Imports System.Configuration
<SettingsProvider(GetType(ElectroZap.CustomSettingsProvider))>
Partial Friend NotInheritable Class MySettings
End Class
SettingsProvider
即可与任何应用一起使用。但这实际上只做两件事:
Point
或
Size
。这些被序列化为不变字符串,以便可以解析它们。这意味着什么:
Console.WriteLine(myPoint.ToString())
{X=64, Y=22}
无法直接转换回,并且
Point
缺少
Parse/TryParse
方法。使用不变的字符串形式
64,22
可以将其转换回正确的类型。原始的链接代码简单地使用了:
Convert.ChangeType(setting.DefaultValue, t);
Point
,
Font
等。我不能肯定地记得,但是我认为这是使用
SettingsPropertyValue.Value
而不是
.SerializedValue
的简单错误。
Public Class CustomSettingsProvider
Inherits SettingsProvider
' data we store for each item
Friend Class SettingsItem
Friend Name As String
'Friend SerializeAs As String ' not needed
Friend Value As String
Friend Roamer As Boolean
Friend Remove As Boolean ' mutable
'Friend VerString As String ' ToDo (?)
End Class
' used for node name
Private thisMachine As String
' loaded XML config
'Private xDoc As XDocument
Private UserConfigFilePath As String = ""
Private myCol As Dictionary(Of String, SettingsItem)
Public Sub New()
myCol = New Dictionary(Of String, SettingsItem)
Dim asm = Assembly.GetExecutingAssembly()
Dim verInfo = FileVersionInfo.GetVersionInfo(asm.Location)
Dim Company = verInfo.CompanyName
' product name may have no relation to file name...
Dim ProdName = verInfo.ProductName
' use this for assembly file name:
Dim modName = Path.GetFileNameWithoutExtension(asm.ManifestModule.Name)
' dont use FileVersionInfo;
' may want to omit the last element
'Dim ver = asm.GetName.Version
' uses `SpecialFolder.ApplicationData`
' since it will store Local and Roaming val;ues
UserConfigFilePath = Path.Combine(GetFolderPath(SpecialFolder.ApplicationData),
Company, modName,
"user.config")
' "CFG" prefix prevents illegal XML,
' the FOO suffix is to emulate a different machine
thisMachine = "CFG" & My.Computer.Name & "_FOO"
End Sub
' boilerplate
Public Overrides Property ApplicationName As String
Get
Return Assembly.GetExecutingAssembly().ManifestModule.Name
End Get
Set(value As String)
End Set
End Property
' boilerplate
Public Overrides Sub Initialize(name As String, config As Specialized.NameValueCollection)
MyBase.Initialize(ApplicationName, config)
End Sub
' conversion helper in place of a 'Select Case GetType(foo)'
Private Shared Conversion As Func(Of Object, Object)
Public Overrides Function GetPropertyValues(context As SettingsContext,
collection As SettingsPropertyCollection) As SettingsPropertyValueCollection
' basically, create a Dictionary entry for each setting,
' store the converted value to it
' Add an entry when something is added
'
' This is called the first time you get a setting value
If myCol.Count = 0 Then
LoadData()
End If
Dim theSettings = New SettingsPropertyValueCollection()
Dim tValue As String = ""
' SettingsPropertyCollection is like a Shopping list
' of props that VS/VB wants the value for
For Each setItem As SettingsProperty In collection
Dim value As New SettingsPropertyValue(setItem)
value.IsDirty = False
If myCol.ContainsKey(setItem.Name) Then
value.SerializedValue = myCol(setItem.Name)
tValue = myCol(setItem.Name).Value
Else
value.SerializedValue = setItem.DefaultValue
tValue = setItem.DefaultValue.ToString
End If
' ToDo: Enums will need an extra step
Conversion = Function(v) TypeDescriptor.
GetConverter(setItem.PropertyType).
ConvertFromInvariantString(v.ToString())
value.PropertyValue = Conversion(tValue)
theSettings.Add(value)
Next
Return theSettings
End Function
Public Overrides Sub SetPropertyValues(context As SettingsContext,
collection As SettingsPropertyValueCollection)
' this is not called when you set a new value
' rather, NET has one or more changed values that
' need to be saved, so be sure to save them to disk
Dim names As List(Of String) = myCol.Keys.ToList
Dim sItem As SettingsItem
For Each item As SettingsPropertyValue In collection
sItem = New SettingsItem() With {
.Name = item.Name,
.Value = item.SerializedValue.ToString(),
.Roamer = IsRoamer(item.Property)
}
'.SerializeAs = item.Property.SerializeAs.ToString(),
names.Remove(item.Name)
If myCol.ContainsKey(sItem.Name) Then
myCol(sItem.Name) = sItem
Else
myCol.Add(sItem.Name, sItem)
End If
Next
' flag any no longer used
' do not use when specifying a provider per-setting!
For Each s As String In names
myCol(s).Remove = True
Next
SaveData()
End Sub
' detect if a setting is tagged as Roaming
Private Function IsRoamer(prop As SettingsProperty) As Boolean
Dim r = prop.Attributes.
Cast(Of DictionaryEntry).
FirstOrDefault(Function(q) TypeOf q.Value Is SettingsManageabilityAttribute)
Return r.Key IsNot Nothing
End Function
Private Sub LoadData()
' load from disk
If File.Exists(UserConfigFilePath) = False Then
CreateNewConfig()
End If
Dim xDoc = XDocument.Load(UserConfigFilePath)
Dim items As IEnumerable(Of XElement)
Dim item As SettingsItem
items = xDoc.Element(CONFIG).
Element(COMMON).
Elements(SETTING)
' load the common settings
For Each xitem As XElement In items
item = New SettingsItem With {.Name = xitem.Attribute(ITEMNAME).Value,
.Roamer = False}
'.SerializeAs = xitem.Attribute(SERIALIZE_AS).Value,
item.Value = xitem.Value
myCol.Add(item.Name, item)
Next
' First check if there is a machine node
If xDoc.Element(CONFIG).Element(thisMachine) Is Nothing Then
' nope, add one
xDoc.Element(CONFIG).Add(New XElement(thisMachine))
End If
items = xDoc.Element(CONFIG).
Element(thisMachine).
Elements(SETTING)
For Each xitem As XElement In items
item = New SettingsItem With {.Name = xitem.Attribute(ITEMNAME).Value,
.Roamer = True}
'.SerializeAs = xitem.Attribute(SERIALIZE_AS).Value,
item.Value = xitem.Value
myCol.Add(item.Name, item)
Next
' we may have changed the XDOC, by adding a machine node
' save the file
xDoc.Save(UserConfigFilePath)
End Sub
Private Sub SaveData()
' write to disk
Dim xDoc = XDocument.Load(UserConfigFilePath)
Dim roamers = xDoc.Element(CONFIG).
Element(thisMachine)
Dim locals = xDoc.Element(CONFIG).
Element(COMMON)
Dim item As XElement
Dim section As XElement
For Each kvp As KeyValuePair(Of String, SettingsItem) In myCol
If kvp.Value.Roamer Then
section = roamers
Else
section = locals
End If
item = section.Elements().
FirstOrDefault(Function(q) q.Attribute(ITEMNAME).Value = kvp.Key)
If item Is Nothing Then
' found a new item
Dim newItem = New XElement(SETTING)
newItem.Add(New XAttribute(ITEMNAME, kvp.Value.Name))
'newItem.Add(New XAttribute(SERIALIZE_AS, kvp.Value.SerializeAs))
newItem.Value = If(String.IsNullOrEmpty(kvp.Value.Value), "", kvp.Value.Value)
section.Add(newItem)
Else
If kvp.Value.Remove Then
item.Remove()
Else
item.Value = If(String.IsNullOrEmpty(kvp.Value.Value), "", kvp.Value.Value)
End If
End If
Next
xDoc.Save(UserConfigFilePath)
End Sub
' used in the XML
Const CONFIG As String = "configuration"
Const SETTING As String = "setting"
Const COMMON As String = "CommonShared"
Const ITEMNAME As String = "name"
'Const SERIALIZE_AS As String = "serializeAs"
' https://stackoverflow.com/a/11398536
Private Sub CreateNewConfig()
Dim fpath = Path.GetDirectoryName(UserConfigFilePath)
Directory.CreateDirectory(fpath)
Dim xDoc = New XDocument
xDoc.Declaration = New XDeclaration("1.0", "utf-8", "true")
Dim cfg = New XElement(CONFIG)
cfg.Add(New XElement(COMMON))
cfg.Add(New XElement(thisMachine))
xDoc.Add(cfg)
xDoc.Save(UserConfigFilePath)
End Sub
End Class
ConfigurationManager
中的属性是只读的,并由代码支持。
IsDirty
标记为true,将
UsingDefaultValue
标记为false。
DataTable
可以使此过程更加简单。您不需要
SettingsItem
类,集合,也不需要XDoc(使用
.WriteXML
/
.ReadXml
)。创建和组织XElement的所有代码也都消失了。
关于c# - 如何更改.NET应用程序的预定义userconfig目录?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38429461/
我对我接管的项目有疑问。我正在转换其他人编写的 MS Access 应用程序并将其转换为 MySQL/PHP Web 应用程序。其中大部分已经完成,但是,当涉及到此应用程序的调度部分时,我处于停滞状态
我有一个带有 @Scheduled 注释的方法。此方法包含长时间运行、昂贵的操作。我担心当计划的方法开始运行时应用程序会变慢。有什么办法可以为预定方法分配优先级吗?在 Spring 中启动低优先级后台
我的大学有一个预订项目房间的网站;但除非你很幸运或者半夜醒着,否则要订到房间并不容易。因此,我编写了一个 JS 片段来填写所有必要的字段并提交表单。 但是我如何自动化这个过程呢? 我的目的基本上是加载
我正在评估处理大量排队消息的可能解决方案,这些消息必须在特定日期和时间交付给工作人员。执行它们的结果主要是对存储数据的更新,它们最初可能是也可能不是由用户操作触发的。 例如,想想你在一个假设的大型星际
@Scheduled documentation here声明 fixedRateString值可以是 the delay in milliseconds as a String value, e.g
关闭。这个问题是opinion-based .它目前不接受答案。 想改善这个问题吗?更新问题,以便可以通过 editing this post 用事实和引文回答问题. 4年前关闭。 Improve t
我有一个有趣的情况。我解析了几个新闻发布网站,想通过调度程序将它们保存到数据库中。但是保存时出现错误。由于交易后写条件 described here . 我的模型类是 @Entity @Table(n
我正在阅读 Java Concurrency in Practice 并遇到以下代码片段。 public static void timedRun(final Runnable r,
使用 Azure 数据工厂,是否可以对驻留在 Azure SQL 数据库中的多个(不是全部)表中的所有行执行预定的 1:1 复制/克隆到另一个 Azure SQL 数据库(在本例中为 Azure SQ
我是一名优秀的程序员,十分优秀!